/*-------------------------------------------------------------------------
 *
 * float.c
 *	  用于内置浮点类型的函数。
 *
 * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 *
 * IDENTIFICATION
 *	  src/backend/utils/adt/float.c
 *
 *-------------------------------------------------------------------------
 */
#include "postgres.h"

#include <ctype.h>
#include <float.h>
#include <math.h>
#include <limits.h>

#include "catalog/pg_type.h"
#include "common/int.h"
#include "common/pg_prng.h"
#include "common/shortest_dec.h"
#include "libpq/pqformat.h"
#include "miscadmin.h"
#include "utils/array.h"
#include "utils/float.h"
#include "utils/fmgrprotos.h"
#include "utils/sortsupport.h"
#include "utils/timestamp.h"


/*
 * 可配置的 GUC 参数
 *
 * 如果 >0，则使用最短的十进制格式作为输出；这是默认设置，并且
 * 允许与在此显式设置值以获得往返准确结果的客户端兼容。如果 0 或更小，
 * 则使用旧的、缓慢的十进制舍入方法。
 */
int			extra_float_digits = 1;

/* 基于度数的三角函数的缓存常量 */
static bool degree_consts_set = false;
static float8 sin_30 = 0;
static float8 one_minus_cos_60 = 0;
static float8 asin_0_5 = 0;
static float8 acos_0_5 = 0;
static float8 atan_1_0 = 0;
static float8 tan_45 = 0;
static float8 cot_45 = 0;

/*
 * 这些故意不是静态的；不要“修复”它们。它们将永远不会
 * 被其他文件引用，更不用说更改；但我们不希望编译器知道这一点，否则它可能会尝试预先计算
 * 涉及它们的表达式。请参见 init_degree_constants() 的注释。
 */
float8		degree_c_thirty = 30.0;
float8		degree_c_forty_five = 45.0;
float8		degree_c_sixty = 60.0;
float8		degree_c_one_half = 0.5;
float8		degree_c_one = 1.0;

/* drandom() 和 setseed() 的状态 */
static bool drandom_seed_set = false;
static pg_prng_state drandom_seed;

/* 本地函数原型 */
static double fc_sind_q1(double fc_x);
static double fc_cosd_q1(double fc_x);
static void fc_init_degree_constants(void);


/*
 * 我们使用这些外部 ereport() 调用来报告浮点溢出、
 * 下溢和零除，因为按照我们通常的做法
 * 在每个调用处重复它们会导致大量代码膨胀。
 *
 * 这确实意味着你不会得到有用的错误位置指示。
 */
pg_noinline void float_overflow_error(void)
{
	ereport(ERROR,
			(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
			 errmsg("value out of range: overflow")));
}

pg_noinline void float_underflow_error(void)
{
	ereport(ERROR,
			(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
			 errmsg("value out of range: underflow")));
}

pg_noinline void float_zero_divide_error(void)
{
	ereport(ERROR,
			(errcode(ERRCODE_DIVISION_BY_ZERO),
			 errmsg("division by zero")));
}


/*
 * 如果 'val' 代表负无穷，则返回 -1；如果 'val'
 * 代表（正）无穷，则返回 1；否则返回 0。在某些平台上，
 * 这相当于 isinf() 宏，但并非所有平台：C99
 * 并未规定 isinf() 需要区分正负无穷。
 */
int is_infinite(double fc_val)
{
	int			fc_inf = isinf(fc_val);

	if (fc_inf == 0)
		return 0;
	else if (fc_val > 0)
		return 1;
	else
		return -1;
}


/* ========== 用户 I/O 例程 ========== */


/*
 *		float4in		- 将 "num" 转换为 float4
 *
 * 请注意，此代码现在使用 strtof()，而之前使用 strtod()。
 *
 * 使用 strtof() 的动机是避免双重舍入问题：
 * 对于某些十进制输入，如果将输入正确舍入为双精度浮点数，
 * 然后再将双精度浮点数舍入为单精度浮点数，结果是不正确的，因为它
 * 与直接将十进制值舍入为单精度浮点数的结果不匹配。
 *
 * 最好的例子之一是 7.038531e-26：
 *
 * 0xAE43FDp-107 = 7.03853069185120912085...e-26
 *      中点   7.03853100000000022281...e-26
 * 0xAE43FEp-107 = 7.03853130814879132477...e-26
 *
 * 使得 0xAE43FDp-107 成为正确的单精度浮点结果，但如果通过双精度浮点数进行转换
 * 则得到
 *
 * 0xAE43FD.7FFFFFF8p-107 = 7.03853099999999907487...e-26
 *               中点   7.03853099999999964884...e-26
 * 0xAE43FD.80000000p-107 = 7.03853100000000022281...e-26
 * 0xAE43FD.80000008p-107 = 7.03853100000000137076...e-26
 *
 * 因此，值恰好在两个最近的单精度浮点数之间的中点处舍入到双精度浮点数，
 * 然后再舍入为单精度浮点数会给出不正确的结果 0xAE43FEp-107。
 *
 */
Datum float4in(PG_FUNCTION_ARGS)
{
	char	   *fc_num = PG_GETARG_CSTRING(0);
	char	   *fc_orig_num;
	float		fc_val;
	char	   *fc_endptr;

	/*
	 * endptr 指向我们识别为有效浮点数的序列之后的第一个字符。
	 * orig_num 指向原始输入字符串。
	 */
	fc_orig_num = fc_num;

	/* 跳过前导空白 */
	while (*fc_num != '\0' && isspace((unsigned char) *fc_num))
		fc_num++;

	/*
	 * 首先检查是否有空字符串输入，以避免在不同平台上
	 * strtod() 的不确定性。
	 */
	if (*fc_num == '\0')
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
				 errmsg("invalid input syntax for type %s: \"%s\"",
						"real", fc_orig_num)));

	errno = 0;
	fc_val = strtof(fc_num, &fc_endptr);

	/* 我们没有看到任何看起来像双精度数的东西吗？ */
	if (fc_endptr == fc_num || errno != 0)
	{
		int			fc_save_errno = errno;

		/*
		 * C99 要求 strtof() 接受 NaN、[+-]Infinity 和 [+-]Inf，
		 * 但并非所有平台都支持所有这些（有些接受它们
		 * 但仍然设置 ERANGE ...）因此，如果 strtof() 失败，
		 * 我们自己检查这些输入。
		 *
		 * 注意：C99 还要求十六进制输入以及某些扩展
		 * 形式的 NaN，但我们认为这些形式不便携，因此不尝试
		 * 支持它们。如果你的 strtof() 可以接受它们，你可以使用。
		 */
		if (pg_strncasecmp(fc_num, "NaN", 3) == 0)
		{
			fc_val = get_float4_nan();
			fc_endptr = fc_num + 3;
		}
		else if (pg_strncasecmp(fc_num, "Infinity", 8) == 0)
		{
			fc_val = get_float4_infinity();
			fc_endptr = fc_num + 8;
		}
		else if (pg_strncasecmp(fc_num, "+Infinity", 9) == 0)
		{
			fc_val = get_float4_infinity();
			fc_endptr = fc_num + 9;
		}
		else if (pg_strncasecmp(fc_num, "-Infinity", 9) == 0)
		{
			fc_val = -get_float4_infinity();
			fc_endptr = fc_num + 9;
		}
		else if (pg_strncasecmp(fc_num, "inf", 3) == 0)
		{
			fc_val = get_float4_infinity();
			fc_endptr = fc_num + 3;
		}
		else if (pg_strncasecmp(fc_num, "+inf", 4) == 0)
		{
			fc_val = get_float4_infinity();
			fc_endptr = fc_num + 4;
		}
		else if (pg_strncasecmp(fc_num, "-inf", 4) == 0)
		{
			fc_val = -get_float4_infinity();
			fc_endptr = fc_num + 4;
		}
		else if (fc_save_errno == ERANGE)
		{
			/*
			 * 一些平台对非规格化数返回 ERANGE（那些
			 * 并非零但太接近零以至于无法获得完整
			 * 精度）。我们希望不对此抛出错误，因此尝试
			 * 检测是否为“真实”的超范围条件，方法是检查
			 * 结果是否为零或很大。
			 *
			 * 在 VS2013 上使用 isinf() 而不是 HUGE_VALF，因为它
			 * 会因 -HUGE_VALF 生成虚假的溢出警告。如果缺少
			 * HUGE_VALF 也使用 isinf()。
			 */
			if (fc_val == 0.0 ||
#if !defined(HUGE_VALF) || (defined(_MSC_VER) && (_MSC_VER < 1900))
				isinf(val)
#else
				(fc_val >= HUGE_VALF || fc_val <= -HUGE_VALF)
#endif
				)
				ereport(ERROR,
						(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
						 errmsg("\"%s\" is out of range for type real",
								fc_orig_num)));
		}
		else
			ereport(ERROR,
					(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
					 errmsg("invalid input syntax for type %s: \"%s\"",
							"real", fc_orig_num)));
	}

	/* 跳过拖尾空白 */
	while (*fc_endptr != '\0' && isspace((unsigned char) *fc_endptr))
		fc_endptr++;

	/* 如果字符串末尾还有任何垃圾，退出 */
	if (*fc_endptr != '\0')
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
				 errmsg("invalid input syntax for type %s: \"%s\"",
						"real", fc_orig_num)));

	PG_RETURN_FLOAT4(fc_val);
}

/*
 *		float4out		- 将 float4 数字转换为字符串
 *						  使用标准输出格式
 */
Datum float4out(PG_FUNCTION_ARGS)
{
	float4		fc_num = PG_GETARG_FLOAT4(0);
	char	   *fc_ascii = (char *) palloc(32);
	int			fc_ndig = FLT_DIG + extra_float_digits;

	if (extra_float_digits > 0)
	{
		float_to_shortest_decimal_buf(fc_num, fc_ascii);
		PG_RETURN_CSTRING(fc_ascii);
	}

	(void) pg_strfromd(fc_ascii, 32, fc_ndig, fc_num);
	PG_RETURN_CSTRING(fc_ascii);
}

/*
 *		float4recv			- 将外部二进制格式转换为 float4
 */
Datum float4recv(PG_FUNCTION_ARGS)
{
	StringInfo	fc_buf = (StringInfo) PG_GETARG_POINTER(0);

	PG_RETURN_FLOAT4(pq_getmsgfloat4(fc_buf));
}

/*
 *		float4send			- 将 float4 转换为二进制格式
 */
Datum float4send(PG_FUNCTION_ARGS)
{
	float4		fc_num = PG_GETARG_FLOAT4(0);
	StringInfoData fc_buf;

	pq_begintypsend(&fc_buf);
	pq_sendfloat4(&fc_buf, fc_num);
	PG_RETURN_BYTEA_P(pq_endtypsend(&fc_buf));
}

/*
 *		float8in		- 将 "num" 转换为 float8
 */
Datum float8in(PG_FUNCTION_ARGS)
{
	char	   *fc_num = PG_GETARG_CSTRING(0);

	PG_RETURN_FLOAT8(float8in_internal(fc_num, NULL, "double precision", fc_num));
}

/* 方便的宏：设置 *have_error 标志（如果提供）或抛出错误 */
#define RETURN_ERROR(throw_error, have_error) \
do { \
	if (have_error) { \
		*have_error = true; \
		return 0.0; \
	} else { \
		throw_error; \
	} \
} while (0)


/*
 * float8in_internal_opt_error - float8in() 的核心部分
 *
 * 这对于希望以合适的跨平台独立方式输入双精度数的函数是公开的。其行为
 * 本质上类似于 strtod + 错误时的 ereport，但请注意以下几点区别：
 * 1. 跳过前后的空白。
 * 2. 如果 endptr_p 为 NULL，则在有尾随垃圾时抛出错误。
 * 否则，是否抱怨尾随垃圾由调用者决定。
 * 3. 如果发生语法错误，报告将提到给定的 type_name，并显示 orig_string 作为输入；
 * 这旨在支持将此函数与类型如 "box" 和 "point" 一起使用，在这种情况下，
 * 我们解析的只是 orig_string 的一个子字符串。
 *
 * "num" 可以有效地声明为 "const char *"，但这会导致在这里和调用者中都需
 * 要不合理的额外强制转换，因此我们不这样做。
 *
 * 当提供 "*have_error" 标志时，会设置它而不是抛出错误。
 * 这在调用者需要自己处理错误时很有帮助。
 */
double float8in_internal_opt_error(char *fc_num, char **fc_endptr_p,
							const char *fc_type_name, const char *fc_orig_string,
							bool *fc_have_error)
{
	double		fc_val;
	char	   *fc_endptr;

	if (fc_have_error)
		*fc_have_error = false;

	/* 跳过前导空白 */
	while (*fc_num != '\0' && isspace((unsigned char) *fc_num))
		fc_num++;

	/*
	 * 首先检查是否有空字符串输入，以避免在不同平台上
	 * strtod() 的不确定性。
	 */
	if (*fc_num == '\0')
		RETURN_ERROR(ereport(ERROR,
							 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
							  errmsg("invalid input syntax for type %s: \"%s\"",
									 fc_type_name, fc_orig_string))),
					 fc_have_error);

	errno = 0;
	fc_val = strtod(fc_num, &fc_endptr);

	/* 我们没有看到任何看起来像双精度数的东西吗？ */
	if (fc_endptr == fc_num || errno != 0)
	{
		int			fc_save_errno = errno;

		/*
		 * C99 要求 strtod() 接受 NaN、[+-]Infinity 和 [+-]Inf，
		 * 但并不是所有平台都支持所有这些（有些平台接受
		 * 但无论如何设置 ERANGE...）。因此，如果 strtod() 失败，
		 * 我们自己检查这些输入。
		 *
		 * 注意：C99 还要求十六进制输入以及某些扩展形式的 NaN，
		 * 但我们认为这些形式不具可移植性，因此不尝试支持它们。
		 * 如果你的 strtod() 接受这些，你可以使用它们。
		 */
		if (pg_strncasecmp(fc_num, "NaN", 3) == 0)
		{
			fc_val = get_float8_nan();
			fc_endptr = fc_num + 3;
		}
		else if (pg_strncasecmp(fc_num, "Infinity", 8) == 0)
		{
			fc_val = get_float8_infinity();
			fc_endptr = fc_num + 8;
		}
		else if (pg_strncasecmp(fc_num, "+Infinity", 9) == 0)
		{
			fc_val = get_float8_infinity();
			fc_endptr = fc_num + 9;
		}
		else if (pg_strncasecmp(fc_num, "-Infinity", 9) == 0)
		{
			fc_val = -get_float8_infinity();
			fc_endptr = fc_num + 9;
		}
		else if (pg_strncasecmp(fc_num, "inf", 3) == 0)
		{
			fc_val = get_float8_infinity();
			fc_endptr = fc_num + 3;
		}
		else if (pg_strncasecmp(fc_num, "+inf", 4) == 0)
		{
			fc_val = get_float8_infinity();
			fc_endptr = fc_num + 4;
		}
		else if (pg_strncasecmp(fc_num, "-inf", 4) == 0)
		{
			fc_val = -get_float8_infinity();
			fc_endptr = fc_num + 4;
		}
		else if (fc_save_errno == ERANGE)
		{
			/*
			 * 一些平台对于非标准化数字返回 ERANGE（那些不是零，
			 * 但太接近零以至于没有完整的精度）。我们不希望对此抛出错误，
			 * 因此尝试通过检查结果是否为零或巨型来检测是否为实际的
			 * 超出范围条件。
			 *
			 * 在错误发生时，我们故意抱怨双精度而非给定类型名称，
			 * 并且我们只打印当前数字的字符串部分。
			 */
			if (fc_val == 0.0 || fc_val >= HUGE_VAL || fc_val <= -HUGE_VAL)
			{
				char	   *fc_errnumber = pstrdup(fc_num);

				fc_errnumber[fc_endptr - fc_num] = '\0';
				RETURN_ERROR(ereport(ERROR,
									 (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
									  errmsg("\"%s\" is out of range for type double precision",
											 fc_errnumber))),
							 fc_have_error);
			}
		}
		else
			RETURN_ERROR(ereport(ERROR,
								 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
								  errmsg("invalid input syntax for type "
										 "%s: \"%s\"",
										 fc_type_name, fc_orig_string))),
						 fc_have_error);
	}

	/* 跳过拖尾空白 */
	while (*fc_endptr != '\0' && isspace((unsigned char) *fc_endptr))
		fc_endptr++;

	/* 如果需要报告停止点，否则如果不是字符串末尾则抱怨 */
	if (fc_endptr_p)
		*fc_endptr_p = fc_endptr;
	else if (*fc_endptr != '\0')
		RETURN_ERROR(ereport(ERROR,
							 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
							  errmsg("invalid input syntax for type "
									 "%s: \"%s\"",
									 fc_type_name, fc_orig_string))),
					 fc_have_error);

	return fc_val;
}

/*
 * 对 float8in_internal_opt_error() 的接口，没有 "have_error" 参数。
 */
double float8in_internal(char *fc_num, char **fc_endptr_p,
				  const char *fc_type_name, const char *fc_orig_string)
{
	return float8in_internal_opt_error(fc_num, fc_endptr_p, fc_type_name,
									   fc_orig_string, NULL);
}


/*
 *		float8out		- 将 float8 数字转换为字符串
 *						  使用标准输出格式
 */
Datum float8out(PG_FUNCTION_ARGS)
{
	float8		fc_num = PG_GETARG_FLOAT8(0);

	PG_RETURN_CSTRING(float8out_internal(fc_num));
}

/*
 * float8out_internal - float8out() 的核心部分
 *
 * 这对于希望以合理的跨平台独立方式输出双精度数的函数是公开的。
 * 结果总是通过 palloc 分配。
 */
char * float8out_internal(double fc_num)
{
	char	   *fc_ascii = (char *) palloc(32);
	int			fc_ndig = DBL_DIG + extra_float_digits;

	if (extra_float_digits > 0)
	{
		double_to_shortest_decimal_buf(fc_num, fc_ascii);
		return fc_ascii;
	}

	(void) pg_strfromd(fc_ascii, 32, fc_ndig, fc_num);
	return fc_ascii;
}

/*
 *		float8recv			- 将外部二进制格式转换为float8
 */
Datum float8recv(PG_FUNCTION_ARGS)
{
	StringInfo	fc_buf = (StringInfo) PG_GETARG_POINTER(0);

	PG_RETURN_FLOAT8(pq_getmsgfloat8(fc_buf));
}

/*
 *		float8send			- 将float8转换为二进制格式
 */
Datum float8send(PG_FUNCTION_ARGS)
{
	float8		fc_num = PG_GETARG_FLOAT8(0);
	StringInfoData fc_buf;

	pq_begintypsend(&fc_buf);
	pq_sendfloat8(&fc_buf, fc_num);
	PG_RETURN_BYTEA_P(pq_endtypsend(&fc_buf));
}


/* ========== 公共例程 ========== */


/*
 *		======================
 *		FLOAT4 基本操作
 *		======================
 */

/*
 *		float4abs		- 返回 |arg1|（绝对值）
 */
Datum float4abs(PG_FUNCTION_ARGS)
{
	float4		fc_arg1 = PG_GETARG_FLOAT4(0);

	PG_RETURN_FLOAT4((float4) fabs(fc_arg1));
}

/*
 *		float4um		- 返回 -arg1（ unary minus）
 */
Datum float4um(PG_FUNCTION_ARGS)
{
	float4		fc_arg1 = PG_GETARG_FLOAT4(0);
	float4		fc_result;

	fc_result = -fc_arg1;
	PG_RETURN_FLOAT4(fc_result);
}

Datum float4up(PG_FUNCTION_ARGS)
{
	float4		fc_arg = PG_GETARG_FLOAT4(0);

	PG_RETURN_FLOAT4(fc_arg);
}

Datum float4larger(PG_FUNCTION_ARGS)
{
	float4		fc_arg1 = PG_GETARG_FLOAT4(0);
	float4		fc_arg2 = PG_GETARG_FLOAT4(1);
	float4		fc_result;

	if (float4_gt(fc_arg1, fc_arg2))
		fc_result = fc_arg1;
	else
		fc_result = fc_arg2;
	PG_RETURN_FLOAT4(fc_result);
}

Datum float4smaller(PG_FUNCTION_ARGS)
{
	float4		fc_arg1 = PG_GETARG_FLOAT4(0);
	float4		fc_arg2 = PG_GETARG_FLOAT4(1);
	float4		fc_result;

	if (float4_lt(fc_arg1, fc_arg2))
		fc_result = fc_arg1;
	else
		fc_result = fc_arg2;
	PG_RETURN_FLOAT4(fc_result);
}

/*
 *		======================
 *		FLOAT8 基本操作
 *		======================
 */

/*
 *		float8abs		- 返回 |arg1|（绝对值）
 */
Datum float8abs(PG_FUNCTION_ARGS)
{
	float8		fc_arg1 = PG_GETARG_FLOAT8(0);

	PG_RETURN_FLOAT8(fabs(fc_arg1));
}


/*
 *		float8um		- 返回 -arg1（ unary minus）
 */
Datum float8um(PG_FUNCTION_ARGS)
{
	float8		fc_arg1 = PG_GETARG_FLOAT8(0);
	float8		fc_result;

	fc_result = -fc_arg1;
	PG_RETURN_FLOAT8(fc_result);
}

Datum float8up(PG_FUNCTION_ARGS)
{
	float8		fc_arg = PG_GETARG_FLOAT8(0);

	PG_RETURN_FLOAT8(fc_arg);
}

Datum float8larger(PG_FUNCTION_ARGS)
{
	float8		fc_arg1 = PG_GETARG_FLOAT8(0);
	float8		fc_arg2 = PG_GETARG_FLOAT8(1);
	float8		fc_result;

	if (float8_gt(fc_arg1, fc_arg2))
		fc_result = fc_arg1;
	else
		fc_result = fc_arg2;
	PG_RETURN_FLOAT8(fc_result);
}

Datum float8smaller(PG_FUNCTION_ARGS)
{
	float8		fc_arg1 = PG_GETARG_FLOAT8(0);
	float8		fc_arg2 = PG_GETARG_FLOAT8(1);
	float8		fc_result;

	if (float8_lt(fc_arg1, fc_arg2))
		fc_result = fc_arg1;
	else
		fc_result = fc_arg2;
	PG_RETURN_FLOAT8(fc_result);
}


/*
 *		====================
 *		算术运算符
 *		====================
 */

/*
 *		float4pl		- 返回 arg1 + arg2
 *		float4mi		- 返回 arg1 - arg2
 *		float4mul		- 返回 arg1 * arg2
 *		float4div		- 返回 arg1 / arg2
 */
Datum float4pl(PG_FUNCTION_ARGS)
{
	float4		fc_arg1 = PG_GETARG_FLOAT4(0);
	float4		fc_arg2 = PG_GETARG_FLOAT4(1);

	PG_RETURN_FLOAT4(float4_pl(fc_arg1, fc_arg2));
}

Datum float4mi(PG_FUNCTION_ARGS)
{
	float4		fc_arg1 = PG_GETARG_FLOAT4(0);
	float4		fc_arg2 = PG_GETARG_FLOAT4(1);

	PG_RETURN_FLOAT4(float4_mi(fc_arg1, fc_arg2));
}

Datum float4mul(PG_FUNCTION_ARGS)
{
	float4		fc_arg1 = PG_GETARG_FLOAT4(0);
	float4		fc_arg2 = PG_GETARG_FLOAT4(1);

	PG_RETURN_FLOAT4(float4_mul(fc_arg1, fc_arg2));
}

Datum float4div(PG_FUNCTION_ARGS)
{
	float4		fc_arg1 = PG_GETARG_FLOAT4(0);
	float4		fc_arg2 = PG_GETARG_FLOAT4(1);

	PG_RETURN_FLOAT4(float4_div(fc_arg1, fc_arg2));
}

/*
 *		float8pl		- 返回 arg1 + arg2
 *		float8mi		- 返回 arg1 - arg2
 *		float8mul		- 返回 arg1 * arg2
 *		float8div		- 返回 arg1 / arg2
 */
Datum float8pl(PG_FUNCTION_ARGS)
{
	float8		fc_arg1 = PG_GETARG_FLOAT8(0);
	float8		fc_arg2 = PG_GETARG_FLOAT8(1);

	PG_RETURN_FLOAT8(float8_pl(fc_arg1, fc_arg2));
}

Datum float8mi(PG_FUNCTION_ARGS)
{
	float8		fc_arg1 = PG_GETARG_FLOAT8(0);
	float8		fc_arg2 = PG_GETARG_FLOAT8(1);

	PG_RETURN_FLOAT8(float8_mi(fc_arg1, fc_arg2));
}

Datum float8mul(PG_FUNCTION_ARGS)
{
	float8		fc_arg1 = PG_GETARG_FLOAT8(0);
	float8		fc_arg2 = PG_GETARG_FLOAT8(1);

	PG_RETURN_FLOAT8(float8_mul(fc_arg1, fc_arg2));
}

Datum float8div(PG_FUNCTION_ARGS)
{
	float8		fc_arg1 = PG_GETARG_FLOAT8(0);
	float8		fc_arg2 = PG_GETARG_FLOAT8(1);

	PG_RETURN_FLOAT8(float8_div(fc_arg1, fc_arg2));
}


/*
 *		====================
 *		比较运算符
 *		====================
 */

/*
 *		float4{eq,ne,lt,le,gt,ge}		- float4/float4 比较操作
 */
int float4_cmp_internal(float4 fc_a, float4 fc_b)
{
	if (float4_gt(fc_a, fc_b))
		return 1;
	if (float4_lt(fc_a, fc_b))
		return -1;
	return 0;
}

Datum float4eq(PG_FUNCTION_ARGS)
{
	float4		fc_arg1 = PG_GETARG_FLOAT4(0);
	float4		fc_arg2 = PG_GETARG_FLOAT4(1);

	PG_RETURN_BOOL(float4_eq(fc_arg1, fc_arg2));
}

Datum float4ne(PG_FUNCTION_ARGS)
{
	float4		fc_arg1 = PG_GETARG_FLOAT4(0);
	float4		fc_arg2 = PG_GETARG_FLOAT4(1);

	PG_RETURN_BOOL(float4_ne(fc_arg1, fc_arg2));
}

Datum float4lt(PG_FUNCTION_ARGS)
{
	float4		fc_arg1 = PG_GETARG_FLOAT4(0);
	float4		fc_arg2 = PG_GETARG_FLOAT4(1);

	PG_RETURN_BOOL(float4_lt(fc_arg1, fc_arg2));
}

Datum float4le(PG_FUNCTION_ARGS)
{
	float4		fc_arg1 = PG_GETARG_FLOAT4(0);
	float4		fc_arg2 = PG_GETARG_FLOAT4(1);

	PG_RETURN_BOOL(float4_le(fc_arg1, fc_arg2));
}

Datum float4gt(PG_FUNCTION_ARGS)
{
	float4		fc_arg1 = PG_GETARG_FLOAT4(0);
	float4		fc_arg2 = PG_GETARG_FLOAT4(1);

	PG_RETURN_BOOL(float4_gt(fc_arg1, fc_arg2));
}

Datum float4ge(PG_FUNCTION_ARGS)
{
	float4		fc_arg1 = PG_GETARG_FLOAT4(0);
	float4		fc_arg2 = PG_GETARG_FLOAT4(1);

	PG_RETURN_BOOL(float4_ge(fc_arg1, fc_arg2));
}

Datum btfloat4cmp(PG_FUNCTION_ARGS)
{
	float4		fc_arg1 = PG_GETARG_FLOAT4(0);
	float4		fc_arg2 = PG_GETARG_FLOAT4(1);

	PG_RETURN_INT32(float4_cmp_internal(fc_arg1, fc_arg2));
}

static int fc_btfloat4fastcmp(Datum fc_x, Datum fc_y, SortSupport fc_ssup)
{
	float4		fc_arg1 = DatumGetFloat4(fc_x);
	float4		fc_arg2 = DatumGetFloat4(fc_y);

	return float4_cmp_internal(fc_arg1, fc_arg2);
}

Datum btfloat4sortsupport(PG_FUNCTION_ARGS)
{
	SortSupport fc_ssup = (SortSupport) PG_GETARG_POINTER(0);

	fc_ssup->comparator = fc_btfloat4fastcmp;
	PG_RETURN_VOID();
}

/*
 *		float8{eq,ne,lt,le,gt,ge}		- float8/float8 比较操作
 */
int float8_cmp_internal(float8 fc_a, float8 fc_b)
{
	if (float8_gt(fc_a, fc_b))
		return 1;
	if (float8_lt(fc_a, fc_b))
		return -1;
	return 0;
}

Datum float8eq(PG_FUNCTION_ARGS)
{
	float8		fc_arg1 = PG_GETARG_FLOAT8(0);
	float8		fc_arg2 = PG_GETARG_FLOAT8(1);

	PG_RETURN_BOOL(float8_eq(fc_arg1, fc_arg2));
}

Datum float8ne(PG_FUNCTION_ARGS)
{
	float8		fc_arg1 = PG_GETARG_FLOAT8(0);
	float8		fc_arg2 = PG_GETARG_FLOAT8(1);

	PG_RETURN_BOOL(float8_ne(fc_arg1, fc_arg2));
}

Datum float8lt(PG_FUNCTION_ARGS)
{
	float8		fc_arg1 = PG_GETARG_FLOAT8(0);
	float8		fc_arg2 = PG_GETARG_FLOAT8(1);

	PG_RETURN_BOOL(float8_lt(fc_arg1, fc_arg2));
}

Datum float8le(PG_FUNCTION_ARGS)
{
	float8		fc_arg1 = PG_GETARG_FLOAT8(0);
	float8		fc_arg2 = PG_GETARG_FLOAT8(1);

	PG_RETURN_BOOL(float8_le(fc_arg1, fc_arg2));
}

Datum float8gt(PG_FUNCTION_ARGS)
{
	float8		fc_arg1 = PG_GETARG_FLOAT8(0);
	float8		fc_arg2 = PG_GETARG_FLOAT8(1);

	PG_RETURN_BOOL(float8_gt(fc_arg1, fc_arg2));
}

Datum float8ge(PG_FUNCTION_ARGS)
{
	float8		fc_arg1 = PG_GETARG_FLOAT8(0);
	float8		fc_arg2 = PG_GETARG_FLOAT8(1);

	PG_RETURN_BOOL(float8_ge(fc_arg1, fc_arg2));
}

Datum btfloat8cmp(PG_FUNCTION_ARGS)
{
	float8		fc_arg1 = PG_GETARG_FLOAT8(0);
	float8		fc_arg2 = PG_GETARG_FLOAT8(1);

	PG_RETURN_INT32(float8_cmp_internal(fc_arg1, fc_arg2));
}

static int fc_btfloat8fastcmp(Datum fc_x, Datum fc_y, SortSupport fc_ssup)
{
	float8		fc_arg1 = DatumGetFloat8(fc_x);
	float8		fc_arg2 = DatumGetFloat8(fc_y);

	return float8_cmp_internal(fc_arg1, fc_arg2);
}

Datum btfloat8sortsupport(PG_FUNCTION_ARGS)
{
	SortSupport fc_ssup = (SortSupport) PG_GETARG_POINTER(0);

	fc_ssup->comparator = fc_btfloat8fastcmp;
	PG_RETURN_VOID();
}

Datum btfloat48cmp(PG_FUNCTION_ARGS)
{
	float4		fc_arg1 = PG_GETARG_FLOAT4(0);
	float8		fc_arg2 = PG_GETARG_FLOAT8(1);

	/* 将float4扩展到float8，然后进行比较 */
	PG_RETURN_INT32(float8_cmp_internal(fc_arg1, fc_arg2));
}

Datum btfloat84cmp(PG_FUNCTION_ARGS)
{
	float8		fc_arg1 = PG_GETARG_FLOAT8(0);
	float4		fc_arg2 = PG_GETARG_FLOAT4(1);

	/* 将float4扩展到float8，然后进行比较 */
	PG_RETURN_INT32(float8_cmp_internal(fc_arg1, fc_arg2));
}

/*
 * in_range 支持函数，适用于float8。
 *
 * 注意：我们无需提供float8_float4变体，因为偏移量值的隐式转换
 * 已很好地处理了该场景。
 */
Datum in_range_float8_float8(PG_FUNCTION_ARGS)
{
	float8		fc_val = PG_GETARG_FLOAT8(0);
	float8		fc_base = PG_GETARG_FLOAT8(1);
	float8		fc_offset = PG_GETARG_FLOAT8(2);
	bool		fc_sub = PG_GETARG_BOOL(3);
	bool		fc_less = PG_GETARG_BOOL(4);
	float8		fc_sum;

	/*
	 * 拒绝负值或NaN偏移量。负值是根据规范，NaN是因为
	 * 适当的语义似乎并不明显。
	 */
	if (isnan(fc_offset) || fc_offset < 0)
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_PRECEDING_OR_FOLLOWING_SIZE),
				 errmsg("invalid preceding or following size in window function")));

	/*
	 * 处理val和/或base为NaN的情况，遵循NaN在非NaN之后排序的规则（参见 float8_cmp_internal）。偏移量不会
	 * 影响结论。
	 */
	if (isnan(fc_val))
	{
		if (isnan(fc_base))
			PG_RETURN_BOOL(true);	/* NAN = NAN */
		else
			PG_RETURN_BOOL(!fc_less);	/* NAN > 非NAN */
	}
	else if (isnan(fc_base))
	{
		PG_RETURN_BOOL(fc_less);	/* 非NAN < NAN */
	}

	/*
	 * 处理base和offset都为无限的情况，计算
	 * base +/- offset将产生NaN。这对应于边界无限提前 +inf 或无限延续 -inf 的窗口框架，
	 * 是不明确的。为了与涉及无限的其他情况（例如+inf无限跟随+inf）保持一致，我们
	 * 选择假设+inf无限提前+inf，-inf无限跟随-inf，因而所有有限和无限值都位于
	 * 这样的窗口框架内。
	 *
	 * 偏移量已知为正值，因此在
	 * 此测试中我们只需检查base的符号。
	 */
	if (isinf(fc_offset) && isinf(fc_base) &&
		(fc_sub ? fc_base > 0 : fc_base < 0))
		PG_RETURN_BOOL(true);

	/*
	 * 否则，计算base +/- offset应该是安全的。我们信任
	 * FPU能够处理输入是+/-inf或者真实和将溢出，产生适当地签名的无限值，
	 * 无论那是否是无限值，都能与val正确比较。
	 */
	if (fc_sub)
		fc_sum = fc_base - fc_offset;
	else
		fc_sum = fc_base + fc_offset;

	if (fc_less)
		PG_RETURN_BOOL(fc_val <= fc_sum);
	else
		PG_RETURN_BOOL(fc_val >= fc_sum);
}

/*
 * in_range 支持函数，适用于float4。
 *
 * 我们在任何情况下都需要一个float4_float8变体，因此我们提供它并
 * 让隐式转换处理float4_float4的情况。
 */
Datum in_range_float4_float8(PG_FUNCTION_ARGS)
{
	float4		fc_val = PG_GETARG_FLOAT4(0);
	float4		fc_base = PG_GETARG_FLOAT4(1);
	float8		fc_offset = PG_GETARG_FLOAT8(2);
	bool		fc_sub = PG_GETARG_BOOL(3);
	bool		fc_less = PG_GETARG_BOOL(4);
	float8		fc_sum;

	/*
	 * 拒绝负值或NaN偏移量。负值是根据规范，NaN是因为
	 * 适当的语义似乎并不明显。
	 */
	if (isnan(fc_offset) || fc_offset < 0)
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_PRECEDING_OR_FOLLOWING_SIZE),
				 errmsg("invalid preceding or following size in window function")));

	/*
	 * 处理val和/或base为NaN的情况，遵循NaN在非NaN之后排序的规则（参见 float8_cmp_internal）。偏移量不会
	 * 影响结论。
	 */
	if (isnan(fc_val))
	{
		if (isnan(fc_base))
			PG_RETURN_BOOL(true);	/* NAN = NAN */
		else
			PG_RETURN_BOOL(!fc_less);	/* NAN > 非NAN */
	}
	else if (isnan(fc_base))
	{
		PG_RETURN_BOOL(fc_less);	/* 非NAN < NAN */
	}

	/*
	 * 处理base和offset都为无限的情况，计算
	 * base +/- offset将产生NaN。这对应于边界无限提前 +inf 或无限延续 -inf 的窗口框架，
	 * 是不明确的。为了与涉及无限的其他情况（例如+inf无限跟随+inf）保持一致，我们
	 * 选择假设+inf无限提前+inf，-inf无限跟随-inf，因而所有有限和无限值都位于
	 * 这样的窗口框架内。
	 *
	 * 偏移量已知为正值，因此在
	 * 此测试中我们只需检查base的符号。
	 */
	if (isinf(fc_offset) && isinf(fc_base) &&
		(fc_sub ? fc_base > 0 : fc_base < 0))
		PG_RETURN_BOOL(true);

	/*
	 * 否则，计算base +/- offset应该是安全的。我们信任
	 * FPU能够处理输入是+/-inf或者真实和将溢出，产生适当地签名的无限值，
	 * 无论那是否是无限值，都能与val正确比较。
	 */
	if (fc_sub)
		fc_sum = fc_base - fc_offset;
	else
		fc_sum = fc_base + fc_offset;

	if (fc_less)
		PG_RETURN_BOOL(fc_val <= fc_sum);
	else
		PG_RETURN_BOOL(fc_val >= fc_sum);
}


/*
 *		===================
 *		转换例程
 *		===================
 */

/*
 *		ftod			- 将float4数字转换为float8数字
 */
Datum ftod(PG_FUNCTION_ARGS)
{
	float4		fc_num = PG_GETARG_FLOAT4(0);

	PG_RETURN_FLOAT8((float8) fc_num);
}


/*
 *		dtof			- 将float8数字转换为float4数字
 */
Datum dtof(PG_FUNCTION_ARGS)
{
	float8		fc_num = PG_GETARG_FLOAT8(0);
	float4		fc_result;

	fc_result = (float4) fc_num;
	if (unlikely(isinf(fc_result)) && !isinf(fc_num))
		float_overflow_error();
	if (unlikely(fc_result == 0.0f) && fc_num != 0.0)
		float_underflow_error();

	PG_RETURN_FLOAT4(fc_result);
}


/*
 *		dtoi4			- 将float8数字转换为int4数字
 */
Datum dtoi4(PG_FUNCTION_ARGS)
{
	float8		fc_num = PG_GETARG_FLOAT8(0);

	/*
	 * 去掉输入中的任何小数部分。这是为了避免在将要四舍五入
	 * 到范围中的值时出错。注意假设rint()将会将NaN或Inf原封不动地传递。
	 */
	fc_num = rint(fc_num);

	/* 范围检查 */
	if (unlikely(isnan(fc_num) || !FLOAT8_FITS_IN_INT32(fc_num)))
		ereport(ERROR,
				(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
				 errmsg("integer out of range")));

	PG_RETURN_INT32((int32) fc_num);
}


/*
 *		dtoi2			- 将 float8 数字转换为 int2 数字
 */
Datum dtoi2(PG_FUNCTION_ARGS)
{
	float8		fc_num = PG_GETARG_FLOAT8(0);

	/*
	 * 去掉输入中的任何小数部分。这是为了避免在将要四舍五入
	 * 到范围中的值时出错。注意假设rint()将会将NaN或Inf原封不动地传递。
	 */
	fc_num = rint(fc_num);

	/* 范围检查 */
	if (unlikely(isnan(fc_num) || !FLOAT8_FITS_IN_INT16(fc_num)))
		ereport(ERROR,
				(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
				 errmsg("smallint out of range")));

	PG_RETURN_INT16((int16) fc_num);
}


/*
 *		i4tod			- 将 int4 数字转换为 float8 数字
 */
Datum i4tod(PG_FUNCTION_ARGS)
{
	int32		fc_num = PG_GETARG_INT32(0);

	PG_RETURN_FLOAT8((float8) fc_num);
}


/*
 *		i2tod			- 将 int2 数字转换为 float8 数字
 */
Datum i2tod(PG_FUNCTION_ARGS)
{
	int16		fc_num = PG_GETARG_INT16(0);

	PG_RETURN_FLOAT8((float8) fc_num);
}


/*
 *		ftoi4			- 将 float4 数字转换为 int4 数字
 */
Datum ftoi4(PG_FUNCTION_ARGS)
{
	float4		fc_num = PG_GETARG_FLOAT4(0);

	/*
	 * 去掉输入中的任何小数部分。这是为了避免在将要四舍五入
	 * 到范围中的值时出错。注意假设rint()将会将NaN或Inf原封不动地传递。
	 */
	fc_num = rint(fc_num);

	/* 范围检查 */
	if (unlikely(isnan(fc_num) || !FLOAT4_FITS_IN_INT32(fc_num)))
		ereport(ERROR,
				(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
				 errmsg("integer out of range")));

	PG_RETURN_INT32((int32) fc_num);
}


/*
 *		ftoi2			- 将 float4 数字转换为 int2 数字
 */
Datum ftoi2(PG_FUNCTION_ARGS)
{
	float4		fc_num = PG_GETARG_FLOAT4(0);

	/*
	 * 去掉输入中的任何小数部分。这是为了避免在将要四舍五入
	 * 到范围中的值时出错。注意假设rint()将会将NaN或Inf原封不动地传递。
	 */
	fc_num = rint(fc_num);

	/* 范围检查 */
	if (unlikely(isnan(fc_num) || !FLOAT4_FITS_IN_INT16(fc_num)))
		ereport(ERROR,
				(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
				 errmsg("smallint out of range")));

	PG_RETURN_INT16((int16) fc_num);
}


/*
 *		i4tof			- 将 int4 数字转换为 float4 数字
 */
Datum i4tof(PG_FUNCTION_ARGS)
{
	int32		fc_num = PG_GETARG_INT32(0);

	PG_RETURN_FLOAT4((float4) fc_num);
}


/*
 *		i2tof			- 将 int2 数字转换为 float4 数字
 */
Datum i2tof(PG_FUNCTION_ARGS)
{
	int16		fc_num = PG_GETARG_INT16(0);

	PG_RETURN_FLOAT4((float4) fc_num);
}


/*
 *		=======================
 *		随机 FLOAT8 操作符
 *		=======================
 */

/*
 *		dround			- 返回 ROUND(arg1)
 */
Datum dround(PG_FUNCTION_ARGS)
{
	float8		fc_arg1 = PG_GETARG_FLOAT8(0);

	PG_RETURN_FLOAT8(rint(fc_arg1));
}

/*
 *		dceil			- 返回大于或等于指定浮点数的最小整数
 */
Datum dceil(PG_FUNCTION_ARGS)
{
	float8		fc_arg1 = PG_GETARG_FLOAT8(0);

	PG_RETURN_FLOAT8(ceil(fc_arg1));
}

/*
 *		dfloor			- 返回小于或等于指定浮点数的最大整数
 */
Datum dfloor(PG_FUNCTION_ARGS)
{
	float8		fc_arg1 = PG_GETARG_FLOAT8(0);

	PG_RETURN_FLOAT8(floor(fc_arg1));
}

/*
 *		dsign			- 如果参数小于 0，则返回 -1；如果参数等于 0，则返回 0；如果参数大于 0，则返回 1。
 */
Datum dsign(PG_FUNCTION_ARGS)
{
	float8		fc_arg1 = PG_GETARG_FLOAT8(0);
	float8		fc_result;

	if (fc_arg1 > 0)
		fc_result = 1.0;
	else if (fc_arg1 < 0)
		fc_result = -1.0;
	else
		fc_result = 0.0;

	PG_RETURN_FLOAT8(fc_result);
}

/*
 *		dtrunc			- 返回 arg1 的截断（朝零方向），
 *						  arg1 >= 0 ... 小于或等于 arg1 的最大整数
 *						  arg1 < 0	... 大于或等于 arg1 的最小整数
 */
Datum dtrunc(PG_FUNCTION_ARGS)
{
	float8		fc_arg1 = PG_GETARG_FLOAT8(0);
	float8		fc_result;

	if (fc_arg1 >= 0)
		fc_result = floor(fc_arg1);
	else
		fc_result = -floor(-fc_arg1);

	PG_RETURN_FLOAT8(fc_result);
}


/*
 *		dsqrt			- 返回 arg1 的平方根
 */
Datum dsqrt(PG_FUNCTION_ARGS)
{
	float8		fc_arg1 = PG_GETARG_FLOAT8(0);
	float8		fc_result;

	if (fc_arg1 < 0)
#ifdef FDD //cppcheck
	{
#endif
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_ARGUMENT_FOR_POWER_FUNCTION),
				 errmsg("cannot take square root of a negative number")));
#ifdef FDD //cppcheck
		PG_RETURN_VOID();
	}
#endif
	fc_result = sqrt(fc_arg1);
	if (unlikely(isinf(fc_result)) && !isinf(fc_arg1))
		float_overflow_error();
	if (unlikely(fc_result == 0.0) && fc_arg1 != 0.0)
		float_underflow_error();

	PG_RETURN_FLOAT8(fc_result);
}


/*
 *		dcbrt			- 返回 arg1 的立方根
 */
Datum dcbrt(PG_FUNCTION_ARGS)
{
	float8		fc_arg1 = PG_GETARG_FLOAT8(0);
	float8		fc_result;

	fc_result = cbrt(fc_arg1);
	if (unlikely(isinf(fc_result)) && !isinf(fc_arg1))
		float_overflow_error();
	if (unlikely(fc_result == 0.0) && fc_arg1 != 0.0)
		float_underflow_error();

	PG_RETURN_FLOAT8(fc_result);
}


/*
 *		dpow			- 返回 pow(arg1,arg2)
 */
Datum dpow(PG_FUNCTION_ARGS)
{
	float8		fc_arg1 = PG_GETARG_FLOAT8(0);
	float8		fc_arg2 = PG_GETARG_FLOAT8(1);
	float8		fc_result;

	/*
	 * POSIX 规范说 NaN ^ 0 = 1，1 ^ NaN = 1，而所有其他
	 * NaN 输入的情况都将返回 NaN（没有错误）。许多旧平台
	 * 在这些情况下处理不当，因此需要通过显式
	 * 逻辑处理，而不是依赖 pow(3)。
	 */
	if (isnan(fc_arg1))
	{
		if (isnan(fc_arg2) || fc_arg2 != 0.0)
			PG_RETURN_FLOAT8(get_float8_nan());
		PG_RETURN_FLOAT8(1.0);
	}
	if (isnan(fc_arg2))
	{
		if (fc_arg1 != 1.0)
			PG_RETURN_FLOAT8(get_float8_nan());
		PG_RETURN_FLOAT8(1.0);
	}

	/*
	 * SQL 规范要求我们在某些错误条件下发出特定的 SQLSTATE 错误代码。
	 * 具体来说，我们不为 0 ^ -1 返回除以零的错误代码。
	 */
	if (fc_arg1 == 0 && fc_arg2 < 0)
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_ARGUMENT_FOR_POWER_FUNCTION),
				 errmsg("zero raised to a negative power is undefined")));
	if (fc_arg1 < 0 && floor(fc_arg2) != fc_arg2)
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_ARGUMENT_FOR_POWER_FUNCTION),
				 errmsg("a negative number raised to a non-integer power yields a complex result")));

	/*
	 * 我们也不信任平台的 pow() 处理按 POSIX
	 * 规范的无穷大情况，因此也要显式处理这些情况。先处理
	 * 无穷大 y 更容易，这样也不必在 x 也是无穷大时担心。
	 */
	if (isinf(fc_arg2))
	{
		float8		fc_absx = fabs(fc_arg1);

		if (fc_absx == 1.0)
			fc_result = 1.0;
		else if (fc_arg2 > 0.0)	/* y = +Inf */
		{
			if (fc_absx > 1.0)
				fc_result = fc_arg2;
			else
				fc_result = 0.0;
		}
		else					/* y = -Inf */
		{
			if (fc_absx > 1.0)
				fc_result = 0.0;
			else
				fc_result = -fc_arg2;
		}
	}
	else if (isinf(fc_arg1))
	{
		if (fc_arg2 == 0.0)
			fc_result = 1.0;
		else if (fc_arg1 > 0.0)	/* x = +Inf */
		{
			if (fc_arg2 > 0.0)
				fc_result = fc_arg1;
			else
				fc_result = 0.0;
		}
		else					/* x = -Inf */
		{
			/*
			 * 根据 POSIX，结果的符号取决于 y 是否为奇数。
			 * 由于 x < 0，之前的域检查已经确定 y 为整数。
			 * 如果 y/2 也不是整数，则它是奇数。
			 */
			float8		fc_halfy = fc_arg2 / 2;	/* 应该精确计算 */
			bool		fc_yisoddinteger = (floor(fc_halfy) != fc_halfy);

			if (fc_arg2 > 0.0)
				fc_result = fc_yisoddinteger ? fc_arg1 : -fc_arg1;
			else
				fc_result = fc_yisoddinteger ? -0.0 : 0.0;
		}
	}
	else
	{
		/*
		 * pow() 仅在某些平台上设置 errno，具体取决于它是否
		 * 遵循 _IEEE_、_POSIX_、_XOPEN_ 或 _SVID_，因此我们必须检查
		 * errno 和无效输出值。（我们也不能仅依赖后者；一些旧平台在
		 * 报告溢出时返回一个大但有限的 HUGE_VAL。）
		 */
		errno = 0;
		fc_result = pow(fc_arg1, fc_arg2);
		if (errno == EDOM || isnan(fc_result))
		{
			/*
			 * 我们在上面处理了所有可能的域错误，因此这应该是
			 * 不可能的。然而，旧版 glibc 在 x86 上存在一个错误，
			 * 导致在 abs(y) 大于 2^63 时以这种方式失败：
			 *
			 * https://sourceware.org/bugzilla/show_bug.cgi?id=3866
			 *
			 * 因此，如果我们到这里，就假设 y 是有限但大的（
			 * 足够大以肯定为偶数）。如果 x == 0，结果应该为 0，
			 * 如果 abs(x) == 1.0，结果为 1.0，否则为溢出或下溢错误。
			 */
			if (fc_arg1 == 0.0)
				fc_result = 0.0;	/* 我们已经验证了 y 是正数 */
			else
			{
				float8		fc_absx = fabs(fc_arg1);

				if (fc_absx == 1.0)
					fc_result = 1.0;
				else if (fc_arg2 >= 0.0 ? (fc_absx > 1.0) : (fc_absx < 1.0))
					float_overflow_error();
				else
					float_underflow_error();
			}
		}
		else if (errno == ERANGE)
		{
			if (fc_result != 0.0)
				float_overflow_error();
			else
				float_underflow_error();
		}
		else
		{
			if (unlikely(isinf(fc_result)))
				float_overflow_error();
			if (unlikely(fc_result == 0.0) && fc_arg1 != 0.0)
				float_underflow_error();
		}
	}

	PG_RETURN_FLOAT8(fc_result);
}


/*
 *		dexp			- 返回 arg1 的指数函数
 */
Datum dexp(PG_FUNCTION_ARGS)
{
	float8		fc_arg1 = PG_GETARG_FLOAT8(0);
	float8		fc_result;

	/*
	 * 显式处理 NaN 和 Inf 情况。 这避免了需要假设
	 * 平台的 exp() 在这些情况下符合 POSIX，并消除了
	 * 下面溢出检查的一些边缘情况。
	 */
	if (isnan(fc_arg1))
		fc_result = fc_arg1;
	else if (isinf(fc_arg1))
	{
		/* 根据 POSIX，exp(-Inf) 是 0 */
		fc_result = (fc_arg1 > 0.0) ? fc_arg1 : 0;
	}
	else
	{
		/*
		 * 在某些平台上，exp() 不会设置 errno，而是返回 Inf 或
		 * 0 来报告溢出/下溢；因此，需要测试这两种情况。
		 */
		errno = 0;
		fc_result = exp(fc_arg1);
		if (unlikely(errno == ERANGE))
		{
			if (fc_result != 0.0)
				float_overflow_error();
			else
				float_underflow_error();
		}
		else if (unlikely(isinf(fc_result)))
			float_overflow_error();
		else if (unlikely(fc_result == 0.0))
			float_underflow_error();
	}

	PG_RETURN_FLOAT8(fc_result);
}


/*
 *		dlog1			- 返回 arg1 的自然对数
 */
Datum dlog1(PG_FUNCTION_ARGS)
{
	float8		fc_arg1 = PG_GETARG_FLOAT8(0);
	float8		fc_result;

	/*
	 * 发出特定的 SQLSTATE 错误代码用于 ln()。这是 SQL 标准的要求。
	 */
	if (fc_arg1 == 0.0)
#ifdef FDD //cppcheck
	{
#endif
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_ARGUMENT_FOR_LOG),
				 errmsg("cannot take logarithm of zero")));
#ifdef FDD //cppcheck
		PG_RETURN_VOID();
	}
#endif
	if (fc_arg1 < 0)
#ifdef FDD //cppcheck
	{
#endif
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_ARGUMENT_FOR_LOG),
				 errmsg("cannot take logarithm of a negative number")));
#ifdef FDD //cppcheck
		PG_RETURN_VOID();
	}
#endif
	fc_result = log(fc_arg1);
	if (unlikely(isinf(fc_result)) && !isinf(fc_arg1))
		float_overflow_error();
	if (unlikely(fc_result == 0.0) && fc_arg1 != 1.0)
		float_underflow_error();

	PG_RETURN_FLOAT8(fc_result);
}


/*
 *		dlog10			- 返回 arg1 的以 10 为底的对数
 */
Datum dlog10(PG_FUNCTION_ARGS)
{
	float8		fc_arg1 = PG_GETARG_FLOAT8(0);
	float8		fc_result;

	/*
	 * 发出特定的 SQLSTATE 错误代码用于 log()。SQL 规格没有
	 * 定义 log()，但它确实定义了 ln()，所以在类似的错误条件下发出
	 * 相同的错误代码是有意义的。
	 */
	if (fc_arg1 == 0.0)
#ifdef FDD //cppcheck
	{
#endif
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_ARGUMENT_FOR_LOG),
				 errmsg("cannot take logarithm of zero")));
#ifdef FDD //cppcheck
		PG_RETURN_VOID();
	}
#endif
	if (fc_arg1 < 0)
#ifdef FDD //cppcheck
	{
#endif
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_ARGUMENT_FOR_LOG),
				 errmsg("cannot take logarithm of a negative number")));
#ifdef FDD //cppcheck
		PG_RETURN_VOID();
	}
#endif

	fc_result = log10(fc_arg1);
	if (unlikely(isinf(fc_result)) && !isinf(fc_arg1))
		float_overflow_error();
	if (unlikely(fc_result == 0.0) && fc_arg1 != 1.0)
		float_underflow_error();

	PG_RETURN_FLOAT8(fc_result);
}


/*
 *		dacos			- 返回 arg1 的反余弦（弧度）
 */
Datum dacos(PG_FUNCTION_ARGS)
{
	float8		fc_arg1 = PG_GETARG_FLOAT8(0);
	float8		fc_result;

	/* 根据 POSIX 规格，如果输入为 NaN，则返回 NaN */
	if (isnan(fc_arg1))
		PG_RETURN_FLOAT8(get_float8_nan());

	/*
	 * 反余弦函数的主分支将 [-1, 1] 范围内的值映射到 [0, Pi] 范围内的值，
	 * 因此我们应该拒绝任何超出该范围的输入，并且结果将始终是有限的。
	 */
	if (fc_arg1 < -1.0 || fc_arg1 > 1.0)
		ereport(ERROR,
				(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
				 errmsg("input is out of range")));

	fc_result = acos(fc_arg1);
	if (unlikely(isinf(fc_result)))
		float_overflow_error();

	PG_RETURN_FLOAT8(fc_result);
}


/*
 *		dasin			- 返回 arg1 的反正弦（弧度）
 */
Datum dasin(PG_FUNCTION_ARGS)
{
	float8		fc_arg1 = PG_GETARG_FLOAT8(0);
	float8		fc_result;

	/* 根据 POSIX 规格，如果输入为 NaN，则返回 NaN */
	if (isnan(fc_arg1))
		PG_RETURN_FLOAT8(get_float8_nan());

	/*
	 * 反正弦函数的主分支将 [-1, 1] 范围内的值映射到 [-Pi/2, Pi/2]
	 * 范围内的值，因此我们应该拒绝任何超出该范围的输入，并且结果将始终是有限的。
	 */
	if (fc_arg1 < -1.0 || fc_arg1 > 1.0)
		ereport(ERROR,
				(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
				 errmsg("input is out of range")));

	fc_result = asin(fc_arg1);
	if (unlikely(isinf(fc_result)))
		float_overflow_error();

	PG_RETURN_FLOAT8(fc_result);
}


/*
 *		datan			- 返回 arg1 的反正切（弧度）
 */
Datum datan(PG_FUNCTION_ARGS)
{
	float8		fc_arg1 = PG_GETARG_FLOAT8(0);
	float8		fc_result;

	/* 根据 POSIX 规格，如果输入为 NaN，则返回 NaN */
	if (isnan(fc_arg1))
		PG_RETURN_FLOAT8(get_float8_nan());

	/*
	 * 反正切函数的主分支将所有输入映射到 [-Pi/2, Pi/2] 范围内的值，
	 * 因此即使输入为无穷，结果也应始终是有限的。
	 */
	fc_result = atan(fc_arg1);
	if (unlikely(isinf(fc_result)))
		float_overflow_error();

	PG_RETURN_FLOAT8(fc_result);
}


/*
 *		atan2			- 返回 arg1/arg2 的反正切（弧度）
 */
Datum datan2(PG_FUNCTION_ARGS)
{
	float8		fc_arg1 = PG_GETARG_FLOAT8(0);
	float8		fc_arg2 = PG_GETARG_FLOAT8(1);
	float8		fc_result;

	/* 根据 POSIX 规格，如果任何输入为 NaN，则返回 NaN */
	if (isnan(fc_arg1) || isnan(fc_arg2))
		PG_RETURN_FLOAT8(get_float8_nan());

	/*
	 * atan2 将所有输入映射到 [-Pi, Pi] 范围内的值，因此即使输入是无穷的，
	 * 结果也应始终是有限的。
	 */
	fc_result = atan2(fc_arg1, fc_arg2);
	if (unlikely(isinf(fc_result)))
		float_overflow_error();

	PG_RETURN_FLOAT8(fc_result);
}


/*
 *		dcos			- 返回 arg1 的余弦（弧度）
 */
Datum dcos(PG_FUNCTION_ARGS)
{
	float8		fc_arg1 = PG_GETARG_FLOAT8(0);
	float8		fc_result;

	/* 根据 POSIX 规格，如果输入为 NaN，则返回 NaN */
	if (isnan(fc_arg1))
		PG_RETURN_FLOAT8(get_float8_nan());

	/*
	 * cos() 是周期性的，因此理论上对所有有限输入都可以工作，
	 * 但某些实现可能选择在输入过大以至于结果中没有有效数字时抛出错误。
	 * 因此我们应该检查错误。POSIX 允许通过 errno 或通过 fetestexcept()
	 * 报告错误，但目前我们仅支持检查 errno。
	 * （fetestexcept() 被传闻会在某些平台上过早地报告下溢，因此相信
	 * 它不一定会是净改善。）
	 *
	 * 对于无穷输入，POSIX 规定三角函数应返回域错误；但除非平台通过 errno
	 * 报告，否则我们不会注意到这一点，因此还要显式测试无穷输入。
	 */
	errno = 0;
	fc_result = cos(fc_arg1);
	if (errno != 0 || isinf(fc_arg1))
		ereport(ERROR,
				(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
				 errmsg("input is out of range")));
	if (unlikely(isinf(fc_result)))
		float_overflow_error();

	PG_RETURN_FLOAT8(fc_result);
}


/*
 *		dcot			- 返回 arg1 的余切（弧度）
 */
Datum dcot(PG_FUNCTION_ARGS)
{
	float8		fc_arg1 = PG_GETARG_FLOAT8(0);
	float8		fc_result;

	/* 根据 POSIX 规格，如果输入为 NaN，则返回 NaN */
	if (isnan(fc_arg1))
		PG_RETURN_FLOAT8(get_float8_nan());

	/* 如果输入是无穷的，一定要抛出错误 --- 参见 dcos() */
	errno = 0;
	fc_result = tan(fc_arg1);
	if (errno != 0 || isinf(fc_arg1))
		ereport(ERROR,
				(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
				 errmsg("input is out of range")));

	fc_result = 1.0 / fc_result;
	/* 不检查溢出，因为 cot(0) == Inf */

	PG_RETURN_FLOAT8(fc_result);
}


/*
 *		dsin			- 返回 arg1 的正弦（弧度）
 */
Datum dsin(PG_FUNCTION_ARGS)
{
	float8		fc_arg1 = PG_GETARG_FLOAT8(0);
	float8		fc_result;

	/* 根据 POSIX 规格，如果输入为 NaN，则返回 NaN */
	if (isnan(fc_arg1))
		PG_RETURN_FLOAT8(get_float8_nan());

	/* 如果输入是无穷的，一定要抛出错误 --- 参见 dcos() */
	errno = 0;
	fc_result = sin(fc_arg1);
	if (errno != 0 || isinf(fc_arg1))
		ereport(ERROR,
				(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
				 errmsg("input is out of range")));
	if (unlikely(isinf(fc_result)))
		float_overflow_error();

	PG_RETURN_FLOAT8(fc_result);
}


/*
 *		dtan			- 返回 arg1 的正切（弧度）
 */
Datum dtan(PG_FUNCTION_ARGS)
{
	float8		fc_arg1 = PG_GETARG_FLOAT8(0);
	float8		fc_result;

	/* 根据 POSIX 规格，如果输入为 NaN，则返回 NaN */
	if (isnan(fc_arg1))
		PG_RETURN_FLOAT8(get_float8_nan());

	/* 如果输入是无穷的，一定要抛出错误 --- 参见 dcos() */
	errno = 0;
	fc_result = tan(fc_arg1);
	if (errno != 0 || isinf(fc_arg1))
		ereport(ERROR,
				(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
				 errmsg("input is out of range")));
	/* 不检查溢出，因为 tan(pi/2) == Inf */

	PG_RETURN_FLOAT8(fc_result);
}


/* ========== 基于度的三角函数 ========== */


/*
 * 初始化本文件开头声明的缓存常量（sin_30 等）。我们需要这些常量的事实，
 * 更不用说这种繁琐的初始化方法，是因为某些编译器会使用与运行时不同的
 * sin() 函数预计算诸如 sin(constant) 的表达式。如果我们想要精确结果，
 * 必须确保在基于度的三角函数中使用的任何缩放常量都不是以这种方式计算的。
 * 为此，我们从变量 degree_c_thirty 等计算这些常量，这些常量实际上也是常量，
 * 但编译器无法假设这一点。
 *
 * 我们试图通过这种变通办法预防的其他危险包括编译器可能会重新排列表达式，
 * 或在宽于标准双精度的寄存器中计算某些中间结果的可能性。
 *
 * 在使用这些常量的地方，典型的模式是类似于
 *		volatile float8 sin_x = sin(x * RADIANS_PER_DEGREE);
 *		return (sin_x / sin_30);
 * 我们希望在 x = 30 时，从除法中获得一个精确的 1.0 值。
 * 在具有宽浮点寄存器的机器上，需要使用易变的临时变量，以确保 sin(x) 的结果
 * 向双精度宽度四舍五入，与 sin_30 的值相同。
 * 与 gcc 的实验表明，标记临时变量为易变是确保存储和重新加载实际发生的必要条件；
 * 希望这个技巧在其他编译器上也能有效。
 * （gcc 的文档建议使用 -ffloat-store 编译器开关来确保这一点，但这是编译器特定的，
 * 且它在许多地方使代码变得更加悲观，而我们并不关心这些。）
 */
static void fc_init_degree_constants(void)
{
	sin_30 = sin(degree_c_thirty * RADIANS_PER_DEGREE);
	one_minus_cos_60 = 1.0 - cos(degree_c_sixty * RADIANS_PER_DEGREE);
	asin_0_5 = asin(degree_c_one_half);
	acos_0_5 = acos(degree_c_one_half);
	atan_1_0 = atan(degree_c_one);
	tan_45 = fc_sind_q1(degree_c_forty_five) / fc_cosd_q1(degree_c_forty_five);
	cot_45 = fc_cosd_q1(degree_c_forty_five) / fc_sind_q1(degree_c_forty_five);
	degree_consts_set = true;
}

#define INIT_DEGREE_CONSTANTS() \
do { \
	if (!degree_consts_set) \
		fc_init_degree_constants(); \
} while(0)



/*
 *		asind_q1		- 返回 x 的反正弦值（以度为单位），对于 x 在
 *						  范围 [0, 1] 内。结果是一个在第一象限的角度 --- [0, 90] 度。
 *
 *						  对于 3 个特殊输入（0，0.5 和 1），该
 *						  函数将返回精确值（分别为 0，30 和 90
 *						  度）。
 */
static double fc_asind_q1(double fc_x)
{
	/*
	 * 将反正弦和反余弦函数拼接在一起，适用于范围 [0,
	 * 0.5] 和 (0.5, 1]。下面的每个表达式保证在 x=0.5 时返回
	 * 精确的 30，因此结果在整个范围内是一个连续单调的函数。
	 */
	if (fc_x <= 0.5)
	{
		volatile float8 fc_asin_x = asin(fc_x);

		return (fc_asin_x / asin_0_5) * 30.0;
	}
	else
	{
		volatile float8 fc_acos_x = acos(fc_x);

		return 90.0 - (fc_acos_x / acos_0_5) * 60.0;
	}
}


/*
 *		acosd_q1		- 返回 x 的反余弦值（以度为单位），对于 x 在
 *						  范围 [0, 1] 内。结果是一个在第一象限的角度 --- [0, 90] 度。
 *
 *						  对于 3 个特殊输入（0，0.5 和 1），该
 *						  函数将返回精确值（分别为 0，60 和 90
 *						  度）。
 */
static double fc_acosd_q1(double fc_x)
{
	/*
	 * 将反正弦和反余弦函数拼接在一起，适用于范围 [0,
	 * 0.5] 和 (0.5, 1]。下面的每个表达式保证在 x=0.5 时返回
	 * 精确的 60，因此结果在整个范围内是一个连续单调的函数。
	 */
	if (fc_x <= 0.5)
	{
		volatile float8 fc_asin_x = asin(fc_x);

		return 90.0 - (fc_asin_x / asin_0_5) * 30.0;
	}
	else
	{
		volatile float8 fc_acos_x = acos(fc_x);

		return (fc_acos_x / acos_0_5) * 60.0;
	}
}


/*
 *		dacosd			- 返回 arg1 的反余弦值（以度为单位）
 */
Datum dacosd(PG_FUNCTION_ARGS)
{
	float8		fc_arg1 = PG_GETARG_FLOAT8(0);
	float8		fc_result;

	/* 根据 POSIX 规格，如果输入为 NaN，则返回 NaN */
	if (isnan(fc_arg1))
		PG_RETURN_FLOAT8(get_float8_nan());

	INIT_DEGREE_CONSTANTS();

	/*
	 * 反余弦函数的主分支将范围 [-1, 1] 内的值映射到范围 [0, 180] 的值，
	 * 所以我们应该拒绝任何超出该范围的输入，结果总是有限的。
	 */
	if (fc_arg1 < -1.0 || fc_arg1 > 1.0)
		ereport(ERROR,
				(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
				 errmsg("input is out of range")));

	if (fc_arg1 >= 0.0)
		fc_result = fc_acosd_q1(fc_arg1);
	else
		fc_result = 90.0 + fc_asind_q1(-fc_arg1);

	if (unlikely(isinf(fc_result)))
		float_overflow_error();

	PG_RETURN_FLOAT8(fc_result);
}


/*
 *		dasind			- 返回 arg1 的反正弦值（以度为单位）
 */
Datum dasind(PG_FUNCTION_ARGS)
{
	float8		fc_arg1 = PG_GETARG_FLOAT8(0);
	float8		fc_result;

	/* 根据 POSIX 规格，如果输入为 NaN，则返回 NaN */
	if (isnan(fc_arg1))
		PG_RETURN_FLOAT8(get_float8_nan());

	INIT_DEGREE_CONSTANTS();

	/*
	 * 反正弦函数的主分支将[-1, 1]范围内的值映射到[-90, 90]范围内的值，
	 * 因此我们应该拒绝任何超出该范围的输入，结果将始终是有限的。
	 */
	if (fc_arg1 < -1.0 || fc_arg1 > 1.0)
		ereport(ERROR,
				(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
				 errmsg("input is out of range")));

	if (fc_arg1 >= 0.0)
		fc_result = fc_asind_q1(fc_arg1);
	else
		fc_result = -fc_asind_q1(-fc_arg1);

	if (unlikely(isinf(fc_result)))
		float_overflow_error();

	PG_RETURN_FLOAT8(fc_result);
}


/*
 *		datand			- 返回arg1的反正切（度数）
 */
Datum datand(PG_FUNCTION_ARGS)
{
	float8		fc_arg1 = PG_GETARG_FLOAT8(0);
	float8		fc_result;
	volatile float8 fc_atan_arg1;

	/* 根据 POSIX 规格，如果输入为 NaN，则返回 NaN */
	if (isnan(fc_arg1))
		PG_RETURN_FLOAT8(get_float8_nan());

	INIT_DEGREE_CONSTANTS();

	/*
	 * 反正切函数的主分支将所有输入值映射到[-90, 90]范围内，
	 * 因此结果应始终是有限的，即使输入是无限的。
	 * 此外，我们确保当arg1为1时，结果恰好是45。
	 */
	fc_atan_arg1 = atan(fc_arg1);
	fc_result = (fc_atan_arg1 / atan_1_0) * 45.0;

	if (unlikely(isinf(fc_result)))
		float_overflow_error();

	PG_RETURN_FLOAT8(fc_result);
}


/*
 *		atan2d			- 返回arg1/arg2的反正切（度数）
 */
Datum datan2d(PG_FUNCTION_ARGS)
{
	float8		fc_arg1 = PG_GETARG_FLOAT8(0);
	float8		fc_arg2 = PG_GETARG_FLOAT8(1);
	float8		fc_result;
	volatile float8 fc_atan2_arg1_arg2;

	/* 根据 POSIX 规格，如果任何输入为 NaN，则返回 NaN */
	if (isnan(fc_arg1) || isnan(fc_arg2))
		PG_RETURN_FLOAT8(get_float8_nan());

	INIT_DEGREE_CONSTANTS();

	/*
	 * atan2d将所有输入值映射到[-180, 180]范围内，因此结果应始终是有限的，
	 * 即使输入是无限的。
	 *
	 * 注意：此编码假设atan(1.0)是从atan2()获取精确结果的合适缩放常数。
	 * 这在某些情况下可能会失败，需要我们决定对于哪些输入我们认为
	 * 可以保证精确结果。
	 */
	fc_atan2_arg1_arg2 = atan2(fc_arg1, fc_arg2);
	fc_result = (fc_atan2_arg1_arg2 / atan_1_0) * 45.0;

	if (unlikely(isinf(fc_result)))
		float_overflow_error();

	PG_RETURN_FLOAT8(fc_result);
}


/*
 *		sind_0_to_30	- 返回一个介于0和30度之间角度的正弦。
 *						  当x为0时返回恰好0，当x为30度时返回恰好0.5。
 */
static double fc_sind_0_to_30(double fc_x)
{
	volatile float8 fc_sin_x = sin(fc_x * RADIANS_PER_DEGREE);

	return (fc_sin_x / sin_30) / 2.0;
}


/*
 *		cosd_0_to_60	- 返回一个介于0和60度之间角度的余弦。
 *						  当x为0时返回恰好1，当x为60度时返回恰好0.5。
 */
static double fc_cosd_0_to_60(double fc_x)
{
	volatile float8 fc_one_minus_cos_x = 1.0 - cos(fc_x * RADIANS_PER_DEGREE);

	return 1.0 - (fc_one_minus_cos_x / one_minus_cos_60) / 2.0;
}


/*
 *		sind_q1			- 返回第一象限（0到90度）一个角度的正弦。
 */
static double fc_sind_q1(double fc_x)
{
	/*
	 * 将正弦和余弦函数拼接在[0, 30]和(30, 90]的范围内。
	 * 这些保证在其端点返回精确答案，因此整体结果是一个连续单调函数，
	 * 当x = 0, 30和90度时给出精确结果。
	 */
	if (fc_x <= 30.0)
		return fc_sind_0_to_30(fc_x);
	else
		return fc_cosd_0_to_60(90.0 - fc_x);
}


/*
 *		cosd_q1			- 返回第一象限（0到90度）一个角度的余弦。
 */
static double fc_cosd_q1(double fc_x)
{
	/*
	 * 将正弦和余弦函数拼接在[0, 60]和(60, 90]的范围内。
	 * 这些保证在其端点返回精确答案，因此整体结果是一个连续单调函数，
	 * 当x = 0, 60和90度时给出精确结果。
	 */
	if (fc_x <= 60.0)
		return fc_cosd_0_to_60(fc_x);
	else
		return fc_sind_0_to_30(90.0 - fc_x);
}


/*
 *		dcosd			- 返回arg1的余弦（度数）
 */
Datum dcosd(PG_FUNCTION_ARGS)
{
	float8		fc_arg1 = PG_GETARG_FLOAT8(0);
	float8		fc_result;
	int			fc_sign = 1;

	/*
	 * 根据POSIX规范，如果输入是NaN则返回NaN，如果输入是无限的则抛出错误。
	 */
	if (isnan(fc_arg1))
		PG_RETURN_FLOAT8(get_float8_nan());

	if (isinf(fc_arg1))
		ereport(ERROR,
				(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
				 errmsg("input is out of range")));

	INIT_DEGREE_CONSTANTS();

	/* 将输入范围缩减到[0,90]度 */
	fc_arg1 = fmod(fc_arg1, 360.0);

	if (fc_arg1 < 0.0)
	{
		/* cosd(-x) = cosd(x) */
		fc_arg1 = -fc_arg1;
	}

	if (fc_arg1 > 180.0)
	{
		/* cosd(360-x) = cosd(x) */
		fc_arg1 = 360.0 - fc_arg1;
	}

	if (fc_arg1 > 90.0)
	{
		/* cosd(180-x) = -cosd(x) */
		fc_arg1 = 180.0 - fc_arg1;
		fc_sign = -fc_sign;
	}

	fc_result = fc_sign * fc_cosd_q1(fc_arg1);

	if (unlikely(isinf(fc_result)))
		float_overflow_error();

	PG_RETURN_FLOAT8(fc_result);
}


/*
 *		dcotd			- 返回arg1的余切（度数）
 */
Datum dcotd(PG_FUNCTION_ARGS)
{
	float8		fc_arg1 = PG_GETARG_FLOAT8(0);
	float8		fc_result;
	volatile float8 fc_cot_arg1;
	int			fc_sign = 1;

	/*
	 * 根据POSIX规范，如果输入是NaN则返回NaN，如果输入是无限的则抛出错误。
	 */
	if (isnan(fc_arg1))
		PG_RETURN_FLOAT8(get_float8_nan());

	if (isinf(fc_arg1))
		ereport(ERROR,
				(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
				 errmsg("input is out of range")));

	INIT_DEGREE_CONSTANTS();

	/* 将输入范围缩减到[0,90]度 */
	fc_arg1 = fmod(fc_arg1, 360.0);

	if (fc_arg1 < 0.0)
	{
		/* cotd(-x) = -cotd(x) */
		fc_arg1 = -fc_arg1;
		fc_sign = -fc_sign;
	}

	if (fc_arg1 > 180.0)
	{
		/* cotd(360-x) = -cotd(x) */
		fc_arg1 = 360.0 - fc_arg1;
		fc_sign = -fc_sign;
	}

	if (fc_arg1 > 90.0)
	{
		/* cotd(180-x) = -cotd(x) */
		fc_arg1 = 180.0 - fc_arg1;
		fc_sign = -fc_sign;
	}

	fc_cot_arg1 = fc_cosd_q1(fc_arg1) / fc_sind_q1(fc_arg1);
	fc_result = fc_sign * (fc_cot_arg1 / cot_45);

	/*
	 * 在某些机器上，我们得到cotd(270) = -0，但这并不总是正确。
	 * 为了可移植性，并且因为使用此函数的用户群体可能不想要-0，
	 * 强制其为普通0。
	 */
	if (fc_result == 0.0)
		fc_result = 0.0;

	/* 不检查溢出，因为cotd(0) == 无限 */

	PG_RETURN_FLOAT8(fc_result);
}


/*
 *		dsind			- 返回arg1的正弦（度数）
 */
Datum dsind(PG_FUNCTION_ARGS)
{
	float8		fc_arg1 = PG_GETARG_FLOAT8(0);
	float8		fc_result;
	int			fc_sign = 1;

	/*
	 * 根据POSIX规范，如果输入是NaN则返回NaN，如果输入是无限的则抛出错误。
	 */
	if (isnan(fc_arg1))
		PG_RETURN_FLOAT8(get_float8_nan());

	if (isinf(fc_arg1))
		ereport(ERROR,
				(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
				 errmsg("input is out of range")));

	INIT_DEGREE_CONSTANTS();

	/* 将输入范围缩减到[0,90]度 */
	fc_arg1 = fmod(fc_arg1, 360.0);

	if (fc_arg1 < 0.0)
	{
		/* sind(-x) = -sind(x) */
		fc_arg1 = -fc_arg1;
		fc_sign = -fc_sign;
	}

	if (fc_arg1 > 180.0)
	{
		/* sind(360-x) = -sind(x) */
		fc_arg1 = 360.0 - fc_arg1;
		fc_sign = -fc_sign;
	}

	if (fc_arg1 > 90.0)
	{
		/* sind(180-x) = sind(x) */
		fc_arg1 = 180.0 - fc_arg1;
	}

	fc_result = fc_sign * fc_sind_q1(fc_arg1);

	if (unlikely(isinf(fc_result)))
		float_overflow_error();

	PG_RETURN_FLOAT8(fc_result);
}


/*
 *		dtand			- 返回arg1的正切（度数）
 */
Datum dtand(PG_FUNCTION_ARGS)
{
	float8		fc_arg1 = PG_GETARG_FLOAT8(0);
	float8		fc_result;
	volatile float8 fc_tan_arg1;
	int			fc_sign = 1;

	/*
	 * 根据POSIX规范，如果输入是NaN则返回NaN，如果输入是无限的则抛出错误。
	 */
	if (isnan(fc_arg1))
		PG_RETURN_FLOAT8(get_float8_nan());

	if (isinf(fc_arg1))
		ereport(ERROR,
				(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
				 errmsg("input is out of range")));

	INIT_DEGREE_CONSTANTS();

	/* 将输入范围缩减到[0,90]度 */
	fc_arg1 = fmod(fc_arg1, 360.0);

	if (fc_arg1 < 0.0)
	{
		/* tand(-x) = -tand(x) */
		fc_arg1 = -fc_arg1;
		fc_sign = -fc_sign;
	}

	if (fc_arg1 > 180.0)
	{
		/* tand(360-x) = -tand(x) */
		fc_arg1 = 360.0 - fc_arg1;
		fc_sign = -fc_sign;
	}

	if (fc_arg1 > 90.0)
	{
		/* tand(180-x) = -tand(x) */
		fc_arg1 = 180.0 - fc_arg1;
		fc_sign = -fc_sign;
	}

	fc_tan_arg1 = fc_sind_q1(fc_arg1) / fc_cosd_q1(fc_arg1);
	fc_result = fc_sign * (fc_tan_arg1 / tan_45);

	/*
	 * 在某些机器上，我们得到tand(180) = 负零，但这并不总是
	 * 真实的。为了便携性，并且因为这个
	 * 函数的用户可能不想要负零，强制它为普通零。
	 */
	if (fc_result == 0.0)
		fc_result = 0.0;

	/* 不检查溢出，因为tand(90) == Inf */

	PG_RETURN_FLOAT8(fc_result);
}


/*
 *		degrees		- 返回从弧度转换的度数
 */
Datum degrees(PG_FUNCTION_ARGS)
{
	float8		fc_arg1 = PG_GETARG_FLOAT8(0);

	PG_RETURN_FLOAT8(float8_div(fc_arg1, RADIANS_PER_DEGREE));
}


/*
 *		dpi				- 返回常量PI
 */
Datum dpi(PG_FUNCTION_ARGS)
{
	PG_RETURN_FLOAT8(M_PI);
}


/*
 *		radians		- 返回从度数转换的弧度
 */
Datum radians(PG_FUNCTION_ARGS)
{
	float8		fc_arg1 = PG_GETARG_FLOAT8(0);

	PG_RETURN_FLOAT8(float8_mul(fc_arg1, RADIANS_PER_DEGREE));
}


/* ========== 超越函数 ========== */


/*
 *		dsinh			- 返回arg1的超越正弦
 */
Datum dsinh(PG_FUNCTION_ARGS)
{
	float8		fc_arg1 = PG_GETARG_FLOAT8(0);
	float8		fc_result;

	errno = 0;
	fc_result = sinh(fc_arg1);

	/*
	 * 如果发生ERANGE错误，意味着有一个溢出。对于sinh，
	 * 结果应该是负无穷或正无穷，具体取决于arg1的符号。
	 */
	if (errno == ERANGE)
	{
		if (fc_arg1 < 0)
			fc_result = -get_float8_infinity();
		else
			fc_result = get_float8_infinity();
	}

	PG_RETURN_FLOAT8(fc_result);
}


/*
 *		dcosh			- 返回arg1的超越余弦
 */
Datum dcosh(PG_FUNCTION_ARGS)
{
	float8		fc_arg1 = PG_GETARG_FLOAT8(0);
	float8		fc_result;

	errno = 0;
	fc_result = cosh(fc_arg1);

	/*
	 * 如果发生ERANGE错误，意味着有一个溢出。由于cosh
	 * 始终为正，因此结果始终为正无穷。
	 */
	if (errno == ERANGE)
		fc_result = get_float8_infinity();

	if (unlikely(fc_result == 0.0))
		float_underflow_error();

	PG_RETURN_FLOAT8(fc_result);
}

/*
 *		dtanh			- 返回arg1的超越正切
 */
Datum dtanh(PG_FUNCTION_ARGS)
{
	float8		fc_arg1 = PG_GETARG_FLOAT8(0);
	float8		fc_result;

	/*
	 * 对于tanh，我们不需要errno检查，因为它永远不会溢出。
	 */
	fc_result = tanh(fc_arg1);

	if (unlikely(isinf(fc_result)))
		float_overflow_error();

	PG_RETURN_FLOAT8(fc_result);
}

/*
 *		dasinh			- 返回arg1的反超越正弦
 */
Datum dasinh(PG_FUNCTION_ARGS)
{
	float8		fc_arg1 = PG_GETARG_FLOAT8(0);
	float8		fc_result;

	/*
	 * 对于asinh，我们不需要errno检查，因为它永远不会溢出。
	 */
	fc_result = asinh(fc_arg1);

	PG_RETURN_FLOAT8(fc_result);
}

/*
 *		dacosh			- 返回arg1的反超越余弦
 */
Datum dacosh(PG_FUNCTION_ARGS)
{
	float8		fc_arg1 = PG_GETARG_FLOAT8(0);
	float8		fc_result;

	/*
	 * acosh仅对输入>= 1.0定义。通过我们自己检查这一点，
	 * 我们不必担心检查EDOM错误，这是一件好事，因为一些实现会针对NaN报告。
	 * 否则，不可能发生错误。
	 */
	if (fc_arg1 < 1.0)
		ereport(ERROR,
				(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
				 errmsg("input is out of range")));

	fc_result = acosh(fc_arg1);

	PG_RETURN_FLOAT8(fc_result);
}

/*
 *		datanh			- 返回arg1的反超越正切
 */
Datum datanh(PG_FUNCTION_ARGS)
{
	float8		fc_arg1 = PG_GETARG_FLOAT8(0);
	float8		fc_result;

	/*
	 * atanh仅对-1到1之间的输入定义。通过我们自己检查这一点，
	 * 我们不必担心检查EDOM错误，这是一件好事，因为一些实现会针对NaN报告。
	 */
	if (fc_arg1 < -1.0 || fc_arg1 > 1.0)
		ereport(ERROR,
				(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
				 errmsg("input is out of range")));

	/*
	 * 还要自己处理无限情况；这很有帮助，因为旧的
	 * glibc版本可能会为此产生错误的errno。
	 * 所有其他输入都不会产生错误。
	 */
	if (fc_arg1 == -1.0)
		fc_result = -get_float8_infinity();
	else if (fc_arg1 == 1.0)
		fc_result = get_float8_infinity();
	else
		fc_result = atanh(fc_arg1);

	PG_RETURN_FLOAT8(fc_result);
}


/*
 *		drandom		- 返回一个随机数
 */
Datum drandom(PG_FUNCTION_ARGS)
{
	float8		fc_result;

	/* 如果在这个进程中尚未初始化随机种子，则进行初始化 */
	if (unlikely(!drandom_seed_set))
	{
		/*
		 * 如果可能，使用高质量随机位初始化种子。
		 * 如果由于某种原因失败，我们会退回到基于当前时间和PID的低质量种子。
		 */
		if (unlikely(!pg_prng_strong_seed(&drandom_seed)))
		{
			TimestampTz fc_now = GetCurrentTimestamp();
			uint64		fc_iseed;

			/* 将PID与时间戳中最可预测的位混合 */
			fc_iseed = (uint64) fc_now ^ ((uint64) MyProcPid << 32);
			pg_prng_seed(&drandom_seed, fc_iseed);
		}
		drandom_seed_set = true;
	}

	/* pg_prng_double生成期望的结果范围 [0.0 - 1.0) */
	fc_result = pg_prng_double(&drandom_seed);

	PG_RETURN_FLOAT8(fc_result);
}


/*
 *		setseed		- 设置随机数生成器的种子
 */
Datum setseed(PG_FUNCTION_ARGS)
{
	float8		fc_seed = PG_GETARG_FLOAT8(0);

	if (fc_seed < -1 || fc_seed > 1 || isnan(fc_seed))
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
				 errmsg("setseed parameter %g is out of allowed range [-1,1]",
						fc_seed)));

	pg_prng_fseed(&drandom_seed, fc_seed);
	drandom_seed_set = true;

	PG_RETURN_VOID();
}




/*
 *		=========================
 *		浮点聚合操作符
 *		=========================
 *
 *		float8_accum		- 用于AVG()、方差聚合等的累积。
 *		float4_accum		- 同样，但输入数据为float4。
 *		float8_avg			- 生成float AVG()的最终结果。
 *		float8_var_samp		- 生成float VAR_SAMP()的最终结果。
 *		float8_var_pop		- 生成float VAR_POP()的最终结果。
 *		float8_stddev_samp	- 生成float STDDEV_SAMP()的最终结果。
 *		float8_stddev_pop	- 生成float STDDEV_POP()的最终结果。
 *
 * 这些聚合的简单书本实现通过累积sum(X)和sum(X^2)来工作。
 * 然而，这种方法在最终计算如总体方差（N*sum(X^2) - sum(X)^2）/ N^2等量时
 * 会遭受较大的舍入误差，因为每个中间项都可能非常大，而差值通常相当小。
 *
 * 相反，我们使用Youngs-Cramer算法[1]，该算法通过累积
 * Sx=sum(X)和Sxx=sum((X-Sx/N)^2)来工作，使用一个数值稳定的算法
 * 逐步更新这些量。每个聚合值的最终计算是微不足道的，并且给出更准确的结果
 * （例如，总体方差仅为Sxx/N）。这个算法也相对容易推广，以允许在不损失
 * 精度的情况下进行并行执行（见，举例来说，[2]）。有关更多详细信息，以及
 * 将其与其他算法的比较，请参见[3]。
 *
 * 所有这些聚合的过渡数据类型是一个包含float8的3元素数组，
 * 按顺序保存值N、Sx、Sxx。
 *
 * 请注意，我们将N表示为浮点数，以避免构建特殊数据类型。
 * 只要有合理的浮点实现，N超过大约2 ^ 52时应该不会有准确性损失
 * （到那时，用户肯定已经失去了兴趣...）
 *
 * [1] 一些与选择求和和乘积求和算法相关的结果，
 * E. A. Youngs和E. M. Cramer，《Technometrics》第13卷，第3期，1971年8月。
 *
 * [2] 更新公式和计算样本方差的成对算法，
 * T. F. Chan，G. H. Golub和R. J. LeVeque，《COMPSTAT 1982》。
 *
 * [3] （协）方差的数值稳定并行计算，Erich
 * Schubert和Michael Gertz，第30届国际科学与统计数据库管理会议论文集，2018年。
 */

static float8 *
fc_check_float8_array(ArrayType *fc_transarray, const char *fc_caller, int fc_n)
{
	/*
	 * 我们期望输入是一个N元素浮点数组； 验证这一点。
	 * 我们不需要使用deconstruct_array()，因为数组数据看起来
	 * 就像一个N个float8值的C数组。
	 */
	if (ARR_NDIM(fc_transarray) != 1 ||
		ARR_DIMS(fc_transarray)[0] != fc_n ||
		ARR_HASNULL(fc_transarray) ||
		ARR_ELEMTYPE(fc_transarray) != FLOAT8OID)
		elog(ERROR, "%s: expected %d-element float8 array", fc_caller, fc_n);
	return (float8 *) ARR_DATA_PTR(fc_transarray);
}

/*
 * float8_combine
 *
 * 聚合组合函数用于将两个3字段的
 * 聚合转换数据组合成一个单一的转换数据。
 * 此函数仅在两阶段聚合中使用，
 * 不应在聚合上下文之外调用。
 */
Datum float8_combine(PG_FUNCTION_ARGS)
{
	ArrayType  *fc_transarray1 = PG_GETARG_ARRAYTYPE_P(0);
	ArrayType  *fc_transarray2 = PG_GETARG_ARRAYTYPE_P(1);
	float8	   *fc_transvalues1;
	float8	   *fc_transvalues2;
	float8		fc_N1,
				fc_Sx1,
				fc_Sxx1,
				fc_N2,
				fc_Sx2,
				fc_Sxx2,
				fc_tmp,
				fc_N,
				fc_Sx,
				fc_Sxx;

	fc_transvalues1 = fc_check_float8_array(fc_transarray1, "float8_combine", 3);
	fc_transvalues2 = fc_check_float8_array(fc_transarray2, "float8_combine", 3);

	fc_N1 = fc_transvalues1[0];
	fc_Sx1 = fc_transvalues1[1];
	fc_Sxx1 = fc_transvalues1[2];

	fc_N2 = fc_transvalues2[0];
	fc_Sx2 = fc_transvalues2[1];
	fc_Sxx2 = fc_transvalues2[2];

	/*--------------------
	 * 过渡值结合使用Youngs-Cramer算法的一般化如下：
	 *
	 *	N = N1 + N2
	 *	Sx = Sx1 + Sx2
	 *	Sxx = Sxx1 + Sxx2 + N1 * N2 * (Sx1/N1 - Sx2/N2)^2 / N;
	 *
	 * 处理特殊情况N1 = 0和N2 = 0是值得的，因为这些情况是微不足道的，
	 * 这样我们就不需要担心一般情况下的除零错误。
	 *--------------------
	 */
	if (fc_N1 == 0.0)
	{
		fc_N = fc_N2;
		fc_Sx = fc_Sx2;
		fc_Sxx = fc_Sxx2;
	}
	else if (fc_N2 == 0.0)
	{
		fc_N = fc_N1;
		fc_Sx = fc_Sx1;
		fc_Sxx = fc_Sxx1;
	}
	else
	{
		fc_N = fc_N1 + fc_N2;
		fc_Sx = float8_pl(fc_Sx1, fc_Sx2);
		fc_tmp = fc_Sx1 / fc_N1 - fc_Sx2 / fc_N2;
		fc_Sxx = fc_Sxx1 + fc_Sxx2 + fc_N1 * fc_N2 * fc_tmp * fc_tmp / fc_N;
		if (unlikely(isinf(fc_Sxx)) && !isinf(fc_Sxx1) && !isinf(fc_Sxx2))
			float_overflow_error();
	}

	/*
	 * 如果我们作为聚合被调用，我们可以作弊并在原地修改我们的第一个
	 * 参数以减少 palloc 开销。否则，我们构造一个包含更新过的转换数据的新数组并返回它。
	 */
	if (AggCheckCallContext(fcinfo, NULL))
	{
		fc_transvalues1[0] = fc_N;
		fc_transvalues1[1] = fc_Sx;
		fc_transvalues1[2] = fc_Sxx;

		PG_RETURN_ARRAYTYPE_P(fc_transarray1);
	}
	else
	{
		Datum		fc_transdatums[3];
		ArrayType  *fc_result;

		fc_transdatums[0] = Float8GetDatumFast(fc_N);
		fc_transdatums[1] = Float8GetDatumFast(fc_Sx);
		fc_transdatums[2] = Float8GetDatumFast(fc_Sxx);

		fc_result = construct_array(fc_transdatums, 3,
								 FLOAT8OID,
								 sizeof(float8), FLOAT8PASSBYVAL, TYPALIGN_DOUBLE);

		PG_RETURN_ARRAYTYPE_P(fc_result);
	}
}

Datum float8_accum(PG_FUNCTION_ARGS)
{
	ArrayType  *fc_transarray = PG_GETARG_ARRAYTYPE_P(0);
	float8		fc_newval = PG_GETARG_FLOAT8(1);
	float8	   *fc_transvalues;
	float8		fc_N,
				fc_Sx,
				fc_Sxx,
				fc_tmp;

	fc_transvalues = fc_check_float8_array(fc_transarray, "float8_accum", 3);
	fc_N = fc_transvalues[0];
	fc_Sx = fc_transvalues[1];
	fc_Sxx = fc_transvalues[2];

	/*
	 * 使用 Youngs-Cramer 算法将新值纳入
	 * 转换值。
	 */
	fc_N += 1.0;
	fc_Sx += fc_newval;
	if (fc_transvalues[0] > 0.0)
	{
		fc_tmp = fc_newval * fc_N - fc_Sx;
		fc_Sxx += fc_tmp * fc_tmp / (fc_N * fc_transvalues[0]);

		/*
		 * 溢出检查。只有当有限
		 * 输入导致无限结果时，我们才报告溢出错误。注意，如果任何输入是无限的，Sxx 应该是 NaN，
		 * 所以我们故意防止 Sxx
		 * 变为无限。
		 */
		if (isinf(fc_Sx) || isinf(fc_Sxx))
		{
			if (!isinf(fc_transvalues[1]) && !isinf(fc_newval))
				float_overflow_error();

			fc_Sxx = get_float8_nan();
		}
	}
	else
	{
		/*
		 * 在第一个输入时，我们通常可以将 Sxx 保持为 0。然而，如果
		 * 第一个输入是 Inf 或 NaN，我们最好强制 Sxx 为 NaN；
		 * 否则当没有更多输入时我们将错误地报告方差为零。
		 */
		if (isnan(fc_newval) || isinf(fc_newval))
			fc_Sxx = get_float8_nan();
	}

	/*
	 * 如果我们作为聚合被调用，我们可以作弊并在原地修改我们的第一个
	 * 参数以减少 palloc 开销。否则，我们构造一个包含更新过的转换数据的新数组并返回它。
	 */
	if (AggCheckCallContext(fcinfo, NULL))
	{
		fc_transvalues[0] = fc_N;
		fc_transvalues[1] = fc_Sx;
		fc_transvalues[2] = fc_Sxx;

		PG_RETURN_ARRAYTYPE_P(fc_transarray);
	}
	else
	{
		Datum		fc_transdatums[3];
		ArrayType  *fc_result;

		fc_transdatums[0] = Float8GetDatumFast(fc_N);
		fc_transdatums[1] = Float8GetDatumFast(fc_Sx);
		fc_transdatums[2] = Float8GetDatumFast(fc_Sxx);

		fc_result = construct_array(fc_transdatums, 3,
								 FLOAT8OID,
								 sizeof(float8), FLOAT8PASSBYVAL, TYPALIGN_DOUBLE);

		PG_RETURN_ARRAYTYPE_P(fc_result);
	}
}

Datum float4_accum(PG_FUNCTION_ARGS)
{
	ArrayType  *fc_transarray = PG_GETARG_ARRAYTYPE_P(0);

	/* 作为 float8 进行计算 */
	float8		fc_newval = PG_GETARG_FLOAT4(1);
	float8	   *fc_transvalues;
	float8		fc_N,
				fc_Sx,
				fc_Sxx,
				fc_tmp;

	fc_transvalues = fc_check_float8_array(fc_transarray, "float4_accum", 3);
	fc_N = fc_transvalues[0];
	fc_Sx = fc_transvalues[1];
	fc_Sxx = fc_transvalues[2];

	/*
	 * 使用 Youngs-Cramer 算法将新值纳入
	 * 转换值。
	 */
	fc_N += 1.0;
	fc_Sx += fc_newval;
	if (fc_transvalues[0] > 0.0)
	{
		fc_tmp = fc_newval * fc_N - fc_Sx;
		fc_Sxx += fc_tmp * fc_tmp / (fc_N * fc_transvalues[0]);

		/*
		 * 溢出检查。只有当有限
		 * 输入导致无限结果时，我们才报告溢出错误。注意，如果任何输入是无限的，Sxx 应该是 NaN，
		 * 所以我们故意防止 Sxx
		 * 变为无限。
		 */
		if (isinf(fc_Sx) || isinf(fc_Sxx))
		{
			if (!isinf(fc_transvalues[1]) && !isinf(fc_newval))
				float_overflow_error();

			fc_Sxx = get_float8_nan();
		}
	}
	else
	{
		/*
		 * 在第一个输入时，我们通常可以将 Sxx 保持为 0。然而，如果
		 * 第一个输入是 Inf 或 NaN，我们最好强制 Sxx 为 NaN；
		 * 否则当没有更多输入时我们将错误地报告方差为零。
		 */
		if (isnan(fc_newval) || isinf(fc_newval))
			fc_Sxx = get_float8_nan();
	}

	/*
	 * 如果我们作为聚合被调用，我们可以作弊并在原地修改我们的第一个
	 * 参数以减少 palloc 开销。否则，我们构造一个包含更新过的转换数据的新数组并返回它。
	 */
	if (AggCheckCallContext(fcinfo, NULL))
	{
		fc_transvalues[0] = fc_N;
		fc_transvalues[1] = fc_Sx;
		fc_transvalues[2] = fc_Sxx;

		PG_RETURN_ARRAYTYPE_P(fc_transarray);
	}
	else
	{
		Datum		fc_transdatums[3];
		ArrayType  *fc_result;

		fc_transdatums[0] = Float8GetDatumFast(fc_N);
		fc_transdatums[1] = Float8GetDatumFast(fc_Sx);
		fc_transdatums[2] = Float8GetDatumFast(fc_Sxx);

		fc_result = construct_array(fc_transdatums, 3,
								 FLOAT8OID,
								 sizeof(float8), FLOAT8PASSBYVAL, TYPALIGN_DOUBLE);

		PG_RETURN_ARRAYTYPE_P(fc_result);
	}
}

Datum float8_avg(PG_FUNCTION_ARGS)
{
	ArrayType  *fc_transarray = PG_GETARG_ARRAYTYPE_P(0);
	float8	   *fc_transvalues;
	float8		fc_N,
				fc_Sx;

	fc_transvalues = fc_check_float8_array(fc_transarray, "float8_avg", 3);
	fc_N = fc_transvalues[0];
	fc_Sx = fc_transvalues[1];
	/* 忽略 Sxx */

	/* SQL 定义零值的 AVG 为 NULL */
	if (fc_N == 0.0)
		PG_RETURN_NULL();

	PG_RETURN_FLOAT8(fc_Sx / fc_N);
}

Datum float8_var_pop(PG_FUNCTION_ARGS)
{
	ArrayType  *fc_transarray = PG_GETARG_ARRAYTYPE_P(0);
	float8	   *fc_transvalues;
	float8		fc_N,
				fc_Sxx;

	fc_transvalues = fc_check_float8_array(fc_transarray, "float8_var_pop", 3);
	fc_N = fc_transvalues[0];
	/* 忽略 Sx */
	fc_Sxx = fc_transvalues[2];

	/* 当 N 为 0 时，整体方差未定义，因此返回 NULL */
	if (fc_N == 0.0)
		PG_RETURN_NULL();

	/* 注意 Sxx 保证为非负 */

	PG_RETURN_FLOAT8(fc_Sxx / fc_N);
}

Datum float8_var_samp(PG_FUNCTION_ARGS)
{
	ArrayType  *fc_transarray = PG_GETARG_ARRAYTYPE_P(0);
	float8	   *fc_transvalues;
	float8		fc_N,
				fc_Sxx;

	fc_transvalues = fc_check_float8_array(fc_transarray, "float8_var_samp", 3);
	fc_N = fc_transvalues[0];
	/* 忽略 Sx */
	fc_Sxx = fc_transvalues[2];

	/* 当 N 为 0 或 1 时，样本方差未定义，因此返回 NULL */
	if (fc_N <= 1.0)
		PG_RETURN_NULL();

	/* 注意 Sxx 保证为非负 */

	PG_RETURN_FLOAT8(fc_Sxx / (fc_N - 1.0));
}

Datum float8_stddev_pop(PG_FUNCTION_ARGS)
{
	ArrayType  *fc_transarray = PG_GETARG_ARRAYTYPE_P(0);
	float8	   *fc_transvalues;
	float8		fc_N,
				fc_Sxx;

	fc_transvalues = fc_check_float8_array(fc_transarray, "float8_stddev_pop", 3);
	fc_N = fc_transvalues[0];
	/* 忽略 Sx */
	fc_Sxx = fc_transvalues[2];

	/* 当 N 为 0 时，整体标准差未定义，因此返回 NULL */
	if (fc_N == 0.0)
		PG_RETURN_NULL();

	/* 注意 Sxx 保证为非负 */

	PG_RETURN_FLOAT8(sqrt(fc_Sxx / fc_N));
}

Datum float8_stddev_samp(PG_FUNCTION_ARGS)
{
	ArrayType  *fc_transarray = PG_GETARG_ARRAYTYPE_P(0);
	float8	   *fc_transvalues;
	float8		fc_N,
				fc_Sxx;

	fc_transvalues = fc_check_float8_array(fc_transarray, "float8_stddev_samp", 3);
	fc_N = fc_transvalues[0];
	/* 忽略 Sx */
	fc_Sxx = fc_transvalues[2];

	/* 当 N 为 0 或 1 时，样本标准差未定义，因此返回 NULL */
	if (fc_N <= 1.0)
		PG_RETURN_NULL();

	/* 注意 Sxx 保证为非负 */

	PG_RETURN_FLOAT8(sqrt(fc_Sxx / (fc_N - 1.0)));
}

/*
 *		=========================
 *		SQL2003 二进制聚合
 *		=========================
 *
 * 与前面的聚合一样，我们使用 Youngs-Cramer 算法来
 * 减少聚合最终函数中的舍入误差。
 *
 * 这些聚合的过渡数据类型是一个包含 6 个元素的 array 
 * float8，按顺序保存值 N, Sx=sum(X), Sxx=sum((X-Sx/N)^2), Sy=sum(Y),
 * Syy=sum((Y-Sy/N)^2), Sxy=sum((X-Sx/N)*(Y-Sy/N))。
 *
 * 注意 Y 是所有这些聚合的第一个参数！
 *
 * 通过拥有多个仅计算实际需要的和的累加器函数来优化它可能看起来很有吸引力。
 * 但是在大多数现代机器上，几个额外的浮点乘法与其他每个元组的开销相比是微不足道的，
 * 所以我选择最小化代码空间。
 */

Datum float8_regr_accum(PG_FUNCTION_ARGS)
{
	ArrayType  *fc_transarray = PG_GETARG_ARRAYTYPE_P(0);
	float8		fc_newvalY = PG_GETARG_FLOAT8(1);
	float8		fc_newvalX = PG_GETARG_FLOAT8(2);
	float8	   *fc_transvalues;
	float8		fc_N,
				fc_Sx,
				fc_Sxx,
				fc_Sy,
				fc_Syy,
				fc_Sxy,
				fc_tmpX,
				fc_tmpY,
				fc_scale;

	fc_transvalues = fc_check_float8_array(fc_transarray, "float8_regr_accum", 6);
	fc_N = fc_transvalues[0];
	fc_Sx = fc_transvalues[1];
	fc_Sxx = fc_transvalues[2];
	fc_Sy = fc_transvalues[3];
	fc_Syy = fc_transvalues[4];
	fc_Sxy = fc_transvalues[5];

	/*
	 * 使用 Youngs-Cramer 算法将新值纳入
	 * 转换值。
	 */
	fc_N += 1.0;
	fc_Sx += fc_newvalX;
	fc_Sy += fc_newvalY;
	if (fc_transvalues[0] > 0.0)
	{
		fc_tmpX = fc_newvalX * fc_N - fc_Sx;
		fc_tmpY = fc_newvalY * fc_N - fc_Sy;
		fc_scale = 1.0 / (fc_N * fc_transvalues[0]);
		fc_Sxx += fc_tmpX * fc_tmpX * fc_scale;
		fc_Syy += fc_tmpY * fc_tmpY * fc_scale;
		fc_Sxy += fc_tmpX * fc_tmpY * fc_scale;

		/*
		 * 溢出检查。只有当有限
		 * 输入导致无限结果时，我们才报告溢出错误。注意，如果任何相关输入是无限的，Sxx，Syy 和 Sxy
		 * 应该是 NaN，因此我们故意防止它们变为无限。
		 */
		if (isinf(fc_Sx) || isinf(fc_Sxx) || isinf(fc_Sy) || isinf(fc_Syy) || isinf(fc_Sxy))
		{
			if (((isinf(fc_Sx) || isinf(fc_Sxx)) &&
				 !isinf(fc_transvalues[1]) && !isinf(fc_newvalX)) ||
				((isinf(fc_Sy) || isinf(fc_Syy)) &&
				 !isinf(fc_transvalues[3]) && !isinf(fc_newvalY)) ||
				(isinf(fc_Sxy) &&
				 !isinf(fc_transvalues[1]) && !isinf(fc_newvalX) &&
				 !isinf(fc_transvalues[3]) && !isinf(fc_newvalY)))
				float_overflow_error();

			if (isinf(fc_Sxx))
				fc_Sxx = get_float8_nan();
			if (isinf(fc_Syy))
				fc_Syy = get_float8_nan();
			if (isinf(fc_Sxy))
				fc_Sxy = get_float8_nan();
		}
	}
	else
	{
		/*
		 * 在第一个输入时，我们通常可以将 Sxx 等保持为 0。然而，
		 * 如果第一个输入是 Inf 或 NaN，我们最好强制依赖的
		 * 和为 NaN；否则当没有更多输入时我们将错误地报告方差为零。
		 */
		if (isnan(fc_newvalX) || isinf(fc_newvalX))
			fc_Sxx = fc_Sxy = get_float8_nan();
		if (isnan(fc_newvalY) || isinf(fc_newvalY))
			fc_Syy = fc_Sxy = get_float8_nan();
	}

	/*
	 * 如果我们作为聚合被调用，我们可以作弊并在原地修改我们的第一个
	 * 参数以减少 palloc 开销。否则，我们构造一个包含更新过的转换数据的新数组并返回它。
	 */
	if (AggCheckCallContext(fcinfo, NULL))
	{
		fc_transvalues[0] = fc_N;
		fc_transvalues[1] = fc_Sx;
		fc_transvalues[2] = fc_Sxx;
		fc_transvalues[3] = fc_Sy;
		fc_transvalues[4] = fc_Syy;
		fc_transvalues[5] = fc_Sxy;

		PG_RETURN_ARRAYTYPE_P(fc_transarray);
	}
	else
	{
		Datum		fc_transdatums[6];
		ArrayType  *fc_result;

		fc_transdatums[0] = Float8GetDatumFast(fc_N);
		fc_transdatums[1] = Float8GetDatumFast(fc_Sx);
		fc_transdatums[2] = Float8GetDatumFast(fc_Sxx);
		fc_transdatums[3] = Float8GetDatumFast(fc_Sy);
		fc_transdatums[4] = Float8GetDatumFast(fc_Syy);
		fc_transdatums[5] = Float8GetDatumFast(fc_Sxy);

		fc_result = construct_array(fc_transdatums, 6,
								 FLOAT8OID,
								 sizeof(float8), FLOAT8PASSBYVAL, TYPALIGN_DOUBLE);

		PG_RETURN_ARRAYTYPE_P(fc_result);
	}
}

/*
 * float8_regr_combine
 *
 * 用于将两个 6 字段
 * 聚合过渡数据合并为单个过渡数据的聚合合并函数。
 * 此函数仅在两阶段聚合中使用，不应在聚合上下文之外调用。
 */
Datum float8_regr_combine(PG_FUNCTION_ARGS)
{
	ArrayType  *fc_transarray1 = PG_GETARG_ARRAYTYPE_P(0);
	ArrayType  *fc_transarray2 = PG_GETARG_ARRAYTYPE_P(1);
	float8	   *fc_transvalues1;
	float8	   *fc_transvalues2;
	float8		fc_N1,
				fc_Sx1,
				fc_Sxx1,
				fc_Sy1,
				fc_Syy1,
				fc_Sxy1,
				fc_N2,
				fc_Sx2,
				fc_Sxx2,
				fc_Sy2,
				fc_Syy2,
				fc_Sxy2,
				fc_tmp1,
				fc_tmp2,
				fc_N,
				fc_Sx,
				fc_Sxx,
				fc_Sy,
				fc_Syy,
				fc_Sxy;

	fc_transvalues1 = fc_check_float8_array(fc_transarray1, "float8_regr_combine", 6);
	fc_transvalues2 = fc_check_float8_array(fc_transarray2, "float8_regr_combine", 6);

	fc_N1 = fc_transvalues1[0];
	fc_Sx1 = fc_transvalues1[1];
	fc_Sxx1 = fc_transvalues1[2];
	fc_Sy1 = fc_transvalues1[3];
	fc_Syy1 = fc_transvalues1[4];
	fc_Sxy1 = fc_transvalues1[5];

	fc_N2 = fc_transvalues2[0];
	fc_Sx2 = fc_transvalues2[1];
	fc_Sxx2 = fc_transvalues2[2];
	fc_Sy2 = fc_transvalues2[3];
	fc_Syy2 = fc_transvalues2[4];
	fc_Sxy2 = fc_transvalues2[5];

	/*--------------------
	 * 转换值使用 Youngs-Cramer 算法的广义形式进行组合，如下所示：
	 *
	 *	N = N1 + N2
	 *	Sx = Sx1 + Sx2
	 *	Sxx = Sxx1 + Sxx2 + N1 * N2 * (Sx1/N1 - Sx2/N2)^2 / N
	 *	Sy = Sy1 + Sy2
	 *	Syy = Syy1 + Syy2 + N1 * N2 * (Sy1/N1 - Sy2/N2)^2 / N
	 *	Sxy = Sxy1 + Sxy2 + N1 * N2 * (Sx1/N1 - Sx2/N2) * (Sy1/N1 - Sy2/N2) / N
	 *
	 * 值得单独处理特例 N1 = 0 和 N2 = 0，
	 * 因为这些情况是简单的，因此我们不需要担心一般情况下的
	 * 除以零错误。
	 *--------------------
	 */
	if (fc_N1 == 0.0)
	{
		fc_N = fc_N2;
		fc_Sx = fc_Sx2;
		fc_Sxx = fc_Sxx2;
		fc_Sy = fc_Sy2;
		fc_Syy = fc_Syy2;
		fc_Sxy = fc_Sxy2;
	}
	else if (fc_N2 == 0.0)
	{
		fc_N = fc_N1;
		fc_Sx = fc_Sx1;
		fc_Sxx = fc_Sxx1;
		fc_Sy = fc_Sy1;
		fc_Syy = fc_Syy1;
		fc_Sxy = fc_Sxy1;
	}
	else
	{
		fc_N = fc_N1 + fc_N2;
		fc_Sx = float8_pl(fc_Sx1, fc_Sx2);
		fc_tmp1 = fc_Sx1 / fc_N1 - fc_Sx2 / fc_N2;
		fc_Sxx = fc_Sxx1 + fc_Sxx2 + fc_N1 * fc_N2 * fc_tmp1 * fc_tmp1 / fc_N;
		if (unlikely(isinf(fc_Sxx)) && !isinf(fc_Sxx1) && !isinf(fc_Sxx2))
			float_overflow_error();
		fc_Sy = float8_pl(fc_Sy1, fc_Sy2);
		fc_tmp2 = fc_Sy1 / fc_N1 - fc_Sy2 / fc_N2;
		fc_Syy = fc_Syy1 + fc_Syy2 + fc_N1 * fc_N2 * fc_tmp2 * fc_tmp2 / fc_N;
		if (unlikely(isinf(fc_Syy)) && !isinf(fc_Syy1) && !isinf(fc_Syy2))
			float_overflow_error();
		fc_Sxy = fc_Sxy1 + fc_Sxy2 + fc_N1 * fc_N2 * fc_tmp1 * fc_tmp2 / fc_N;
		if (unlikely(isinf(fc_Sxy)) && !isinf(fc_Sxy1) && !isinf(fc_Sxy2))
			float_overflow_error();
	}

	/*
	 * 如果我们作为聚合被调用，我们可以作弊并在原地修改我们的第一个
	 * 参数以减少 palloc 开销。否则，我们构造一个包含更新过的转换数据的新数组并返回它。
	 */
	if (AggCheckCallContext(fcinfo, NULL))
	{
		fc_transvalues1[0] = fc_N;
		fc_transvalues1[1] = fc_Sx;
		fc_transvalues1[2] = fc_Sxx;
		fc_transvalues1[3] = fc_Sy;
		fc_transvalues1[4] = fc_Syy;
		fc_transvalues1[5] = fc_Sxy;

		PG_RETURN_ARRAYTYPE_P(fc_transarray1);
	}
	else
	{
		Datum		fc_transdatums[6];
		ArrayType  *fc_result;

		fc_transdatums[0] = Float8GetDatumFast(fc_N);
		fc_transdatums[1] = Float8GetDatumFast(fc_Sx);
		fc_transdatums[2] = Float8GetDatumFast(fc_Sxx);
		fc_transdatums[3] = Float8GetDatumFast(fc_Sy);
		fc_transdatums[4] = Float8GetDatumFast(fc_Syy);
		fc_transdatums[5] = Float8GetDatumFast(fc_Sxy);

		fc_result = construct_array(fc_transdatums, 6,
								 FLOAT8OID,
								 sizeof(float8), FLOAT8PASSBYVAL, TYPALIGN_DOUBLE);

		PG_RETURN_ARRAYTYPE_P(fc_result);
	}
}


Datum float8_regr_sxx(PG_FUNCTION_ARGS)
{
	ArrayType  *fc_transarray = PG_GETARG_ARRAYTYPE_P(0);
	float8	   *fc_transvalues;
	float8		fc_N,
				fc_Sxx;

	fc_transvalues = fc_check_float8_array(fc_transarray, "float8_regr_sxx", 6);
	fc_N = fc_transvalues[0];
	fc_Sxx = fc_transvalues[2];

	/* 如果 N 为 0，我们应返回 NULL */
	if (fc_N < 1.0)
		PG_RETURN_NULL();

	/* 注意 Sxx 保证为非负 */

	PG_RETURN_FLOAT8(fc_Sxx);
}

Datum float8_regr_syy(PG_FUNCTION_ARGS)
{
	ArrayType  *fc_transarray = PG_GETARG_ARRAYTYPE_P(0);
	float8	   *fc_transvalues;
	float8		fc_N,
				fc_Syy;

	fc_transvalues = fc_check_float8_array(fc_transarray, "float8_regr_syy", 6);
	fc_N = fc_transvalues[0];
	fc_Syy = fc_transvalues[4];

	/* 如果 N 为 0，我们应返回 NULL */
	if (fc_N < 1.0)
		PG_RETURN_NULL();

	/* 注意 Syy 保证为非负 */

	PG_RETURN_FLOAT8(fc_Syy);
}

Datum float8_regr_sxy(PG_FUNCTION_ARGS)
{
	ArrayType  *fc_transarray = PG_GETARG_ARRAYTYPE_P(0);
	float8	   *fc_transvalues;
	float8		fc_N,
				fc_Sxy;

	fc_transvalues = fc_check_float8_array(fc_transarray, "float8_regr_sxy", 6);
	fc_N = fc_transvalues[0];
	fc_Sxy = fc_transvalues[5];

	/* 如果 N 为 0，我们应返回 NULL */
	if (fc_N < 1.0)
		PG_RETURN_NULL();

	/* 负结果在这里是有效的 */

	PG_RETURN_FLOAT8(fc_Sxy);
}

Datum float8_regr_avgx(PG_FUNCTION_ARGS)
{
	ArrayType  *fc_transarray = PG_GETARG_ARRAYTYPE_P(0);
	float8	   *fc_transvalues;
	float8		fc_N,
				fc_Sx;

	fc_transvalues = fc_check_float8_array(fc_transarray, "float8_regr_avgx", 6);
	fc_N = fc_transvalues[0];
	fc_Sx = fc_transvalues[1];

	/* 如果 N 为 0，我们应返回 NULL */
	if (fc_N < 1.0)
		PG_RETURN_NULL();

	PG_RETURN_FLOAT8(fc_Sx / fc_N);
}

Datum float8_regr_avgy(PG_FUNCTION_ARGS)
{
	ArrayType  *fc_transarray = PG_GETARG_ARRAYTYPE_P(0);
	float8	   *fc_transvalues;
	float8		fc_N,
				fc_Sy;

	fc_transvalues = fc_check_float8_array(fc_transarray, "float8_regr_avgy", 6);
	fc_N = fc_transvalues[0];
	fc_Sy = fc_transvalues[3];

	/* 如果 N 为 0，我们应返回 NULL */
	if (fc_N < 1.0)
		PG_RETURN_NULL();

	PG_RETURN_FLOAT8(fc_Sy / fc_N);
}

Datum float8_covar_pop(PG_FUNCTION_ARGS)
{
	ArrayType  *fc_transarray = PG_GETARG_ARRAYTYPE_P(0);
	float8	   *fc_transvalues;
	float8		fc_N,
				fc_Sxy;

	fc_transvalues = fc_check_float8_array(fc_transarray, "float8_covar_pop", 6);
	fc_N = fc_transvalues[0];
	fc_Sxy = fc_transvalues[5];

	/* 如果 N 为 0，我们应返回 NULL */
	if (fc_N < 1.0)
		PG_RETURN_NULL();

	PG_RETURN_FLOAT8(fc_Sxy / fc_N);
}

Datum float8_covar_samp(PG_FUNCTION_ARGS)
{
	ArrayType  *fc_transarray = PG_GETARG_ARRAYTYPE_P(0);
	float8	   *fc_transvalues;
	float8		fc_N,
				fc_Sxy;

	fc_transvalues = fc_check_float8_array(fc_transarray, "float8_covar_samp", 6);
	fc_N = fc_transvalues[0];
	fc_Sxy = fc_transvalues[5];

	/* 如果 N <= 1，我们应返回 NULL */
	if (fc_N < 2.0)
		PG_RETURN_NULL();

	PG_RETURN_FLOAT8(fc_Sxy / (fc_N - 1.0));
}

Datum float8_corr(PG_FUNCTION_ARGS)
{
	ArrayType  *fc_transarray = PG_GETARG_ARRAYTYPE_P(0);
	float8	   *fc_transvalues;
	float8		fc_N,
				fc_Sxx,
				fc_Syy,
				fc_Sxy;

	fc_transvalues = fc_check_float8_array(fc_transarray, "float8_corr", 6);
	fc_N = fc_transvalues[0];
	fc_Sxx = fc_transvalues[2];
	fc_Syy = fc_transvalues[4];
	fc_Sxy = fc_transvalues[5];

	/* 如果 N 为 0，我们应返回 NULL */
	if (fc_N < 1.0)
		PG_RETURN_NULL();

	/* 注意 Sxx 和 Syy 保证为非负 */

	/* 按规范，对于水平和垂直线返回 NULL */
	if (fc_Sxx == 0 || fc_Syy == 0)
		PG_RETURN_NULL();

	PG_RETURN_FLOAT8(fc_Sxy / sqrt(fc_Sxx * fc_Syy));
}

Datum float8_regr_r2(PG_FUNCTION_ARGS)
{
	ArrayType  *fc_transarray = PG_GETARG_ARRAYTYPE_P(0);
	float8	   *fc_transvalues;
	float8		fc_N,
				fc_Sxx,
				fc_Syy,
				fc_Sxy;

	fc_transvalues = fc_check_float8_array(fc_transarray, "float8_regr_r2", 6);
	fc_N = fc_transvalues[0];
	fc_Sxx = fc_transvalues[2];
	fc_Syy = fc_transvalues[4];
	fc_Sxy = fc_transvalues[5];

	/* 如果 N 为 0，我们应返回 NULL */
	if (fc_N < 1.0)
		PG_RETURN_NULL();

	/* 注意 Sxx 和 Syy 保证为非负 */

	/* 按规范，对于垂直线返回 NULL */
	if (fc_Sxx == 0)
		PG_RETURN_NULL();

	/* 按规范，对于水平线返回 1.0 */
	if (fc_Syy == 0)
		PG_RETURN_FLOAT8(1.0);

	PG_RETURN_FLOAT8((fc_Sxy * fc_Sxy) / (fc_Sxx * fc_Syy));
}

Datum float8_regr_slope(PG_FUNCTION_ARGS)
{
	ArrayType  *fc_transarray = PG_GETARG_ARRAYTYPE_P(0);
	float8	   *fc_transvalues;
	float8		fc_N,
				fc_Sxx,
				fc_Sxy;

	fc_transvalues = fc_check_float8_array(fc_transarray, "float8_regr_slope", 6);
	fc_N = fc_transvalues[0];
	fc_Sxx = fc_transvalues[2];
	fc_Sxy = fc_transvalues[5];

	/* 如果 N 为 0，我们应返回 NULL */
	if (fc_N < 1.0)
		PG_RETURN_NULL();

	/* 注意 Sxx 保证为非负 */

	/* 按规范，对于垂直线返回 NULL */
	if (fc_Sxx == 0)
		PG_RETURN_NULL();

	PG_RETURN_FLOAT8(fc_Sxy / fc_Sxx);
}

Datum float8_regr_intercept(PG_FUNCTION_ARGS)
{
	ArrayType  *fc_transarray = PG_GETARG_ARRAYTYPE_P(0);
	float8	   *fc_transvalues;
	float8		fc_N,
				fc_Sx,
				fc_Sxx,
				fc_Sy,
				fc_Sxy;

	fc_transvalues = fc_check_float8_array(fc_transarray, "float8_regr_intercept", 6);
	fc_N = fc_transvalues[0];
	fc_Sx = fc_transvalues[1];
	fc_Sxx = fc_transvalues[2];
	fc_Sy = fc_transvalues[3];
	fc_Sxy = fc_transvalues[5];

	/* 如果 N 为 0，我们应返回 NULL */
	if (fc_N < 1.0)
		PG_RETURN_NULL();

	/* 注意 Sxx 保证为非负 */

	/* 按规范，对于垂直线返回 NULL */
	if (fc_Sxx == 0)
		PG_RETURN_NULL();

	PG_RETURN_FLOAT8((fc_Sy - fc_Sx * fc_Sxy / fc_Sxx) / fc_N);
}


/*
 *		====================================
 *		混合精度算术运算符
 *		====================================
 */

/*
 *		float48pl		- 返回 arg1 + arg2
 *		float48mi		- 返回 arg1 - arg2
 *		float48mul		- 返回 arg1 * arg2
 *		float48div		- 返回 arg1 / arg2
 */
Datum float48pl(PG_FUNCTION_ARGS)
{
	float4		fc_arg1 = PG_GETARG_FLOAT4(0);
	float8		fc_arg2 = PG_GETARG_FLOAT8(1);

	PG_RETURN_FLOAT8(float8_pl((float8) fc_arg1, fc_arg2));
}

Datum float48mi(PG_FUNCTION_ARGS)
{
	float4		fc_arg1 = PG_GETARG_FLOAT4(0);
	float8		fc_arg2 = PG_GETARG_FLOAT8(1);

	PG_RETURN_FLOAT8(float8_mi((float8) fc_arg1, fc_arg2));
}

Datum float48mul(PG_FUNCTION_ARGS)
{
	float4		fc_arg1 = PG_GETARG_FLOAT4(0);
	float8		fc_arg2 = PG_GETARG_FLOAT8(1);

	PG_RETURN_FLOAT8(float8_mul((float8) fc_arg1, fc_arg2));
}

Datum float48div(PG_FUNCTION_ARGS)
{
	float4		fc_arg1 = PG_GETARG_FLOAT4(0);
	float8		fc_arg2 = PG_GETARG_FLOAT8(1);

	PG_RETURN_FLOAT8(float8_div((float8) fc_arg1, fc_arg2));
}

/*
 *		float84pl		- 返回arg1 + arg2
 *		float84mi		- 返回arg1 - arg2
 *		float84mul		- 返回arg1 * arg2
 *		float84div		- 返回arg1 / arg2
 */
Datum float84pl(PG_FUNCTION_ARGS)
{
	float8		fc_arg1 = PG_GETARG_FLOAT8(0);
	float4		fc_arg2 = PG_GETARG_FLOAT4(1);

	PG_RETURN_FLOAT8(float8_pl(fc_arg1, (float8) fc_arg2));
}

Datum float84mi(PG_FUNCTION_ARGS)
{
	float8		fc_arg1 = PG_GETARG_FLOAT8(0);
	float4		fc_arg2 = PG_GETARG_FLOAT4(1);

	PG_RETURN_FLOAT8(float8_mi(fc_arg1, (float8) fc_arg2));
}

Datum float84mul(PG_FUNCTION_ARGS)
{
	float8		fc_arg1 = PG_GETARG_FLOAT8(0);
	float4		fc_arg2 = PG_GETARG_FLOAT4(1);

	PG_RETURN_FLOAT8(float8_mul(fc_arg1, (float8) fc_arg2));
}

Datum float84div(PG_FUNCTION_ARGS)
{
	float8		fc_arg1 = PG_GETARG_FLOAT8(0);
	float4		fc_arg2 = PG_GETARG_FLOAT4(1);

	PG_RETURN_FLOAT8(float8_div(fc_arg1, (float8) fc_arg2));
}

/*
 *		====================
 *		比较运算符
 *		====================
 */

/*
 *		float48{eq,ne,lt,le,gt,ge}		- float4/float8 比较操作
 */
Datum float48eq(PG_FUNCTION_ARGS)
{
	float4		fc_arg1 = PG_GETARG_FLOAT4(0);
	float8		fc_arg2 = PG_GETARG_FLOAT8(1);

	PG_RETURN_BOOL(float8_eq((float8) fc_arg1, fc_arg2));
}

Datum float48ne(PG_FUNCTION_ARGS)
{
	float4		fc_arg1 = PG_GETARG_FLOAT4(0);
	float8		fc_arg2 = PG_GETARG_FLOAT8(1);

	PG_RETURN_BOOL(float8_ne((float8) fc_arg1, fc_arg2));
}

Datum float48lt(PG_FUNCTION_ARGS)
{
	float4		fc_arg1 = PG_GETARG_FLOAT4(0);
	float8		fc_arg2 = PG_GETARG_FLOAT8(1);

	PG_RETURN_BOOL(float8_lt((float8) fc_arg1, fc_arg2));
}

Datum float48le(PG_FUNCTION_ARGS)
{
	float4		fc_arg1 = PG_GETARG_FLOAT4(0);
	float8		fc_arg2 = PG_GETARG_FLOAT8(1);

	PG_RETURN_BOOL(float8_le((float8) fc_arg1, fc_arg2));
}

Datum float48gt(PG_FUNCTION_ARGS)
{
	float4		fc_arg1 = PG_GETARG_FLOAT4(0);
	float8		fc_arg2 = PG_GETARG_FLOAT8(1);

	PG_RETURN_BOOL(float8_gt((float8) fc_arg1, fc_arg2));
}

Datum float48ge(PG_FUNCTION_ARGS)
{
	float4		fc_arg1 = PG_GETARG_FLOAT4(0);
	float8		fc_arg2 = PG_GETARG_FLOAT8(1);

	PG_RETURN_BOOL(float8_ge((float8) fc_arg1, fc_arg2));
}

/*
 *		float84{eq,ne,lt,le,gt,ge}		- float8/float4 比较操作
 */
Datum float84eq(PG_FUNCTION_ARGS)
{
	float8		fc_arg1 = PG_GETARG_FLOAT8(0);
	float4		fc_arg2 = PG_GETARG_FLOAT4(1);

	PG_RETURN_BOOL(float8_eq(fc_arg1, (float8) fc_arg2));
}

Datum float84ne(PG_FUNCTION_ARGS)
{
	float8		fc_arg1 = PG_GETARG_FLOAT8(0);
	float4		fc_arg2 = PG_GETARG_FLOAT4(1);

	PG_RETURN_BOOL(float8_ne(fc_arg1, (float8) fc_arg2));
}

Datum float84lt(PG_FUNCTION_ARGS)
{
	float8		fc_arg1 = PG_GETARG_FLOAT8(0);
	float4		fc_arg2 = PG_GETARG_FLOAT4(1);

	PG_RETURN_BOOL(float8_lt(fc_arg1, (float8) fc_arg2));
}

Datum float84le(PG_FUNCTION_ARGS)
{
	float8		fc_arg1 = PG_GETARG_FLOAT8(0);
	float4		fc_arg2 = PG_GETARG_FLOAT4(1);

	PG_RETURN_BOOL(float8_le(fc_arg1, (float8) fc_arg2));
}

Datum float84gt(PG_FUNCTION_ARGS)
{
	float8		fc_arg1 = PG_GETARG_FLOAT8(0);
	float4		fc_arg2 = PG_GETARG_FLOAT4(1);

	PG_RETURN_BOOL(float8_gt(fc_arg1, (float8) fc_arg2));
}

Datum float84ge(PG_FUNCTION_ARGS)
{
	float8		fc_arg1 = PG_GETARG_FLOAT8(0);
	float4		fc_arg2 = PG_GETARG_FLOAT4(1);

	PG_RETURN_BOOL(float8_ge(fc_arg1, (float8) fc_arg2));
}

/*
 * 实现 SQL2003 定义的 width_bucket() 函数的 float8 版本。有关 width_bucket_numeric() 的信息请参见。
 *
 * 'bound1' 和 'bound2' 分别是直方图范围的下限和上限。'count' 是直方图中的桶数。width_bucket() 返回一个整数，表示 'operand' 在具有指定特征的等宽直方图中所属的桶号。小于下限的操作数被分配到桶 0。大于上限的操作数被分配到一个额外的桶（编号为 count + 1）。我们不允许 float8 输入的任何 "NaN"，也不允许直方图的上下限为 +/- 无穷大。
 */
Datum width_bucket_float8(PG_FUNCTION_ARGS)
{
	float8		fc_operand = PG_GETARG_FLOAT8(0);
	float8		fc_bound1 = PG_GETARG_FLOAT8(1);
	float8		fc_bound2 = PG_GETARG_FLOAT8(2);
	int32		fc_count = PG_GETARG_INT32(3);
	int32		fc_result;

	if (fc_count <= 0.0)
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_ARGUMENT_FOR_WIDTH_BUCKET_FUNCTION),
				 errmsg("count must be greater than zero")));

	if (isnan(fc_operand) || isnan(fc_bound1) || isnan(fc_bound2))
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_ARGUMENT_FOR_WIDTH_BUCKET_FUNCTION),
				 errmsg("operand, lower bound, and upper bound cannot be NaN")));

	/* 请注意，我们允许 "operand" 为无穷大 */
	if (isinf(fc_bound1) || isinf(fc_bound2))
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_ARGUMENT_FOR_WIDTH_BUCKET_FUNCTION),
				 errmsg("lower and upper bounds must be finite")));

	if (fc_bound1 < fc_bound2)
	{
		if (fc_operand < fc_bound1)
			fc_result = 0;
		else if (fc_operand >= fc_bound2)
		{
			if (pg_add_s32_overflow(fc_count, 1, &fc_result))
				ereport(ERROR,
						(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
						 errmsg("integer out of range")));
		}
		else
			fc_result = ((float8) fc_count * (fc_operand - fc_bound1) / (fc_bound2 - fc_bound1)) + 1;
	}
	else if (fc_bound1 > fc_bound2)
	{
		if (fc_operand > fc_bound1)
			fc_result = 0;
		else if (fc_operand <= fc_bound2)
		{
			if (pg_add_s32_overflow(fc_count, 1, &fc_result))
				ereport(ERROR,
						(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
						 errmsg("integer out of range")));
		}
		else
			fc_result = ((float8) fc_count * (fc_bound1 - fc_operand) / (fc_bound1 - fc_bound2)) + 1;
	}
	else
	{
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_ARGUMENT_FOR_WIDTH_BUCKET_FUNCTION),
				 errmsg("lower bound cannot equal upper bound")));
		fc_result = 0;				/* 让编译器安静 */
	}

	PG_RETURN_INT32(fc_result);
}
